aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
commit0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch)
tree6709ddd6071a9c238ba69233540bbcfe560c6a44 /procps
parent67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff)
parent56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff)
downloadbusybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts: coreutils/ls.c include/platform.h libbb/bb_basename.c
Diffstat (limited to 'procps')
-rw-r--r--procps/fuser.c386
-rw-r--r--procps/iostat.c335
-rw-r--r--procps/mpstat.c3
-rw-r--r--procps/nmeter.c26
-rw-r--r--procps/powertop.c2
-rw-r--r--procps/top.c282
6 files changed, 513 insertions, 521 deletions
diff --git a/procps/fuser.c b/procps/fuser.c
index 7837ff883..8d63a7313 100644
--- a/procps/fuser.c
+++ b/procps/fuser.c
@@ -37,33 +37,18 @@ typedef struct inode_list {
37 dev_t dev; 37 dev_t dev;
38} inode_list; 38} inode_list;
39 39
40typedef struct pid_list {
41 struct pid_list *next;
42 pid_t pid;
43} pid_list;
44
45
46struct globals { 40struct globals {
47 pid_list *pid_list_head; 41 int recursion_depth;
42 pid_t mypid;
48 inode_list *inode_list_head; 43 inode_list *inode_list_head;
44 smallint kill_failed;
45 int killsig;
49} FIX_ALIASING; 46} FIX_ALIASING;
50#define G (*(struct globals*)&bb_common_bufsiz1) 47#define G (*(struct globals*)&bb_common_bufsiz1)
51#define INIT_G() do { } while (0) 48#define INIT_G() do { \
52 49 G.mypid = getpid(); \
53 50 G.killsig = SIGKILL; \
54static void add_pid(const pid_t pid) 51} while (0)
55{
56 pid_list **curr = &G.pid_list_head;
57
58 while (*curr) {
59 if ((*curr)->pid == pid)
60 return;
61 curr = &(*curr)->next;
62 }
63
64 *curr = xzalloc(sizeof(pid_list));
65 (*curr)->pid = pid;
66}
67 52
68static void add_inode(const struct stat *st) 53static void add_inode(const struct stat *st)
69{ 54{
@@ -83,48 +68,7 @@ static void add_inode(const struct stat *st)
83 (*curr)->inode = st->st_ino; 68 (*curr)->inode = st->st_ino;
84} 69}
85 70
86static void scan_proc_net(const char *path, unsigned port) 71static smallint search_dev_inode(const struct stat *st)
87{
88 char line[MAX_LINE + 1];
89 long long uint64_inode;
90 unsigned tmp_port;
91 FILE *f;
92 struct stat st;
93 int fd;
94
95 /* find socket dev */
96 st.st_dev = 0;
97 fd = socket(AF_INET, SOCK_DGRAM, 0);
98 if (fd >= 0) {
99 fstat(fd, &st);
100 close(fd);
101 }
102
103 f = fopen_for_read(path);
104 if (!f)
105 return;
106
107 while (fgets(line, MAX_LINE, f)) {
108 char addr[68];
109 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
110 "%*x:%*x %*x %*d %*d %llu",
111 addr, &tmp_port, &uint64_inode) == 3
112 ) {
113 int len = strlen(addr);
114 if (len == 8 && (option_mask32 & OPT_IP6))
115 continue;
116 if (len > 8 && (option_mask32 & OPT_IP4))
117 continue;
118 if (tmp_port == port) {
119 st.st_ino = uint64_inode;
120 add_inode(&st);
121 }
122 }
123 }
124 fclose(f);
125}
126
127static int search_dev_inode(const struct stat *st)
128{ 72{
129 inode_list *ilist = G.inode_list_head; 73 inode_list *ilist = G.inode_list_head;
130 74
@@ -140,130 +84,202 @@ static int search_dev_inode(const struct stat *st)
140 return 0; 84 return 0;
141} 85}
142 86
143static void scan_pid_maps(const char *fname, pid_t pid) 87enum {
88 PROC_NET = 0,
89 PROC_DIR,
90 PROC_DIR_LINKS,
91 PROC_SUBDIR_LINKS,
92};
93
94static smallint scan_proc_net_or_maps(const char *path, unsigned port)
144{ 95{
145 FILE *file; 96 FILE *f;
146 char line[MAX_LINE + 1]; 97 char line[MAX_LINE + 1], addr[68];
147 int major, minor; 98 int major, minor, r;
148 long long uint64_inode; 99 long long uint64_inode;
149 struct stat st; 100 unsigned tmp_port;
101 smallint retval;
102 struct stat statbuf;
103 const char *fmt;
104 void *fag, *sag;
150 105
151 file = fopen_for_read(fname); 106 f = fopen_for_read(path);
152 if (!file) 107 if (!f)
153 return; 108 return 0;
154 109
155 while (fgets(line, MAX_LINE, file)) { 110 if (G.recursion_depth == PROC_NET) {
156 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) 111 int fd;
157 continue;
158 st.st_ino = uint64_inode;
159 if (major == 0 && minor == 0 && st.st_ino == 0)
160 continue;
161 st.st_dev = makedev(major, minor);
162 if (search_dev_inode(&st))
163 add_pid(pid);
164 }
165 fclose(file);
166}
167 112
168static void scan_link(const char *lname, pid_t pid) 113 /* find socket dev */
169{ 114 statbuf.st_dev = 0;
170 struct stat st; 115 fd = socket(AF_INET, SOCK_DGRAM, 0);
116 if (fd >= 0) {
117 fstat(fd, &statbuf);
118 close(fd);
119 }
171 120
172 if (stat(lname, &st) >= 0) { 121 fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
173 if (search_dev_inode(&st)) 122 "%*x:%*x %*x:%*x %*x %*d %*d %llu";
174 add_pid(pid); 123 fag = addr;
124 sag = &tmp_port;
125 } else {
126 fmt = "%*s %*s %*s %x:%x %llu";
127 fag = &major;
128 sag = &minor;
175 } 129 }
176}
177
178static void scan_dir_links(const char *dname, pid_t pid)
179{
180 DIR *d;
181 struct dirent *de;
182 char *lname;
183
184 d = opendir(dname);
185 if (!d)
186 return;
187 130
188 while ((de = readdir(d)) != NULL) { 131 retval = 0;
189 lname = concat_subpath_file(dname, de->d_name); 132 while (fgets(line, MAX_LINE, f)) {
190 if (lname == NULL) 133 r = sscanf(line, fmt, fag, sag, &uint64_inode);
134 if (r != 3)
191 continue; 135 continue;
192 scan_link(lname, pid); 136
193 free(lname); 137 statbuf.st_ino = uint64_inode;
138 if (G.recursion_depth == PROC_NET) {
139 r = strlen(addr);
140 if (r == 8 && (option_mask32 & OPT_IP6))
141 continue;
142 if (r > 8 && (option_mask32 & OPT_IP4))
143 continue;
144 if (tmp_port == port)
145 add_inode(&statbuf);
146 } else {
147 if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
148 statbuf.st_dev = makedev(major, minor);
149 retval = search_dev_inode(&statbuf);
150 if (retval)
151 break;
152 }
153 }
194 } 154 }
195 closedir(d); 155 fclose(f);
156
157 return retval;
196} 158}
197 159
198/* NB: does chdir internally */ 160static smallint scan_recursive(const char *path)
199static void scan_proc_pids(void)
200{ 161{
201 DIR *d; 162 DIR *d;
202 struct dirent *de; 163 struct dirent *d_ent;
203 pid_t pid; 164 smallint stop_scan;
204 165 smallint retval;
205 xchdir("/proc"); 166
206 d = opendir("/proc"); 167 d = opendir(path);
207 if (!d) 168 if (d == NULL)
208 return; 169 return 0;
209 170
210 while ((de = readdir(d)) != NULL) { 171 G.recursion_depth++;
211 pid = (pid_t)bb_strtou(de->d_name, NULL, 10); 172 retval = 0;
212 if (errno) 173 stop_scan = 0;
213 continue; 174 while (!stop_scan && (d_ent = readdir(d)) != NULL) {
214 if (chdir(de->d_name) < 0) 175 struct stat statbuf;
215 continue; 176 pid_t pid;
216 scan_link("cwd", pid); 177 char *subpath;
217 scan_link("exe", pid); 178
218 scan_link("root", pid); 179 subpath = concat_subpath_file(path, d_ent->d_name);
219 180 if (subpath == NULL)
220 scan_dir_links("fd", pid); 181 continue; /* . or .. */
221 scan_dir_links("lib", pid); 182
222 scan_dir_links("mmap", pid); 183 switch (G.recursion_depth) {
184 case PROC_DIR:
185 pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
186 if (errno != 0
187 || pid == G.mypid
188 /* "this PID doesn't use specified FILEs or PORT/PROTO": */
189 || scan_recursive(subpath) == 0
190 ) {
191 break;
192 }
193 if (option_mask32 & OPT_KILL) {
194 if (kill(pid, G.killsig) != 0) {
195 bb_perror_msg("kill pid %s", d_ent->d_name);
196 G.kill_failed = 1;
197 }
198 }
199 if (!(option_mask32 & OPT_SILENT))
200 printf("%s ", d_ent->d_name);
201 retval = 1;
202 break;
223 203
224 scan_pid_maps("maps", pid); 204 case PROC_DIR_LINKS:
225 xchdir("/proc"); 205 switch (
206 index_in_substrings(
207 "cwd" "\0" "exe" "\0"
208 "root" "\0" "fd" "\0"
209 "lib" "\0" "mmap" "\0"
210 "maps" "\0",
211 d_ent->d_name
212 )
213 ) {
214 enum {
215 CWD_LINK,
216 EXE_LINK,
217 ROOT_LINK,
218 FD_DIR_LINKS,
219 LIB_DIR_LINKS,
220 MMAP_DIR_LINKS,
221 MAPS,
222 };
223 case CWD_LINK:
224 case EXE_LINK:
225 case ROOT_LINK:
226 goto scan_link;
227 case FD_DIR_LINKS:
228 case LIB_DIR_LINKS:
229 case MMAP_DIR_LINKS:
230 stop_scan = scan_recursive(subpath);
231 if (stop_scan)
232 retval = stop_scan;
233 break;
234 case MAPS:
235 stop_scan = scan_proc_net_or_maps(subpath, 0);
236 if (stop_scan)
237 retval = stop_scan;
238 default:
239 break;
240 }
241 break;
242 case PROC_SUBDIR_LINKS:
243 scan_link:
244 if (stat(subpath, &statbuf) < 0)
245 break;
246 stop_scan = search_dev_inode(&statbuf);
247 if (stop_scan)
248 retval = stop_scan;
249 default:
250 break;
251 }
252 free(subpath);
226 } 253 }
227 closedir(d); 254 closedir(d);
255 G.recursion_depth--;
256 return retval;
228} 257}
229 258
230int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 259int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
231int fuser_main(int argc UNUSED_PARAM, char **argv) 260int fuser_main(int argc UNUSED_PARAM, char **argv)
232{ 261{
233 pid_list *plist;
234 pid_t mypid;
235 char **pp; 262 char **pp;
236 struct stat st; 263
237 unsigned port; 264 INIT_G();
238 int opt; 265
239 int exitcode;
240 int killsig;
241/*
242fuser [OPTIONS] FILE or PORT/PROTO
243Find processes which use FILEs or PORTs
244 -m Find processes which use same fs as FILEs
245 -4 Search only IPv4 space
246 -6 Search only IPv6 space
247 -s Don't display PIDs
248 -k Kill found processes
249 -SIGNAL Signal to send (default: KILL)
250*/
251 /* Handle -SIGNAL. Oh my... */ 266 /* Handle -SIGNAL. Oh my... */
252 killsig = SIGKILL; /* yes, the default is not SIGTERM */
253 pp = argv; 267 pp = argv;
254 while (*++pp) { 268 while (*++pp) {
269 int sig;
255 char *arg = *pp; 270 char *arg = *pp;
271
256 if (arg[0] != '-') 272 if (arg[0] != '-')
257 continue; 273 continue;
258 if (arg[1] == '-' && arg[2] == '\0') /* "--" */ 274 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
259 break; 275 break;
260 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') 276 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
261 continue; /* it's "-4" or "-6" */ 277 continue; /* it's "-4" or "-6" */
262 opt = get_signum(&arg[1]); 278 sig = get_signum(&arg[1]);
263 if (opt < 0) 279 if (sig < 0)
264 continue; 280 continue;
265 /* "-SIGNAL" option found. Remove it and bail out */ 281 /* "-SIGNAL" option found. Remove it and bail out */
266 killsig = opt; 282 G.killsig = sig;
267 do { 283 do {
268 pp[0] = arg = pp[1]; 284 pp[0] = arg = pp[1];
269 pp++; 285 pp++;
@@ -272,57 +288,35 @@ Find processes which use FILEs or PORTs
272 } 288 }
273 289
274 opt_complementary = "-1"; /* at least one param */ 290 opt_complementary = "-1"; /* at least one param */
275 opt = getopt32(argv, OPTION_STRING); 291 getopt32(argv, OPTION_STRING);
276 argv += optind; 292 argv += optind;
277 293
278 pp = argv; 294 pp = argv;
279 while (*pp) { 295 while (*pp) {
280 /* parse net arg */ 296 /* parse net arg */
281 char path[20], tproto[5]; 297 unsigned port;
282 if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) 298 char path[sizeof("/proc/net/TCP6")];
283 goto file; 299
284 sprintf(path, "/proc/net/%s", tproto); 300 strcpy(path, "/proc/net/");
285 if (access(path, R_OK) != 0) { /* PORT/PROTO */ 301 if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
286 scan_proc_net(path, port); 302 && access(path, R_OK) == 0
287 } else { /* FILE */ 303 ) {
288 file: 304 /* PORT/PROTO */
289 xstat(*pp, &st); 305 scan_proc_net_or_maps(path, port);
290 add_inode(&st); 306 } else {
307 /* FILE */
308 struct stat statbuf;
309 xstat(*pp, &statbuf);
310 add_inode(&statbuf);
291 } 311 }
292 pp++; 312 pp++;
293 } 313 }
294 314
295 scan_proc_pids(); /* changes dir to "/proc" */ 315 if (scan_recursive("/proc")) {
296 316 if (!(option_mask32 & OPT_SILENT))
297 mypid = getpid(); 317 bb_putchar('\n');
298 plist = G.pid_list_head; 318 return G.kill_failed;
299 while (1) {
300 if (!plist)
301 return EXIT_FAILURE;
302 if (plist->pid != mypid)
303 break;
304 plist = plist->next;
305 }
306
307 exitcode = EXIT_SUCCESS;
308 do {
309 if (plist->pid != mypid) {
310 if (opt & OPT_KILL) {
311 if (kill(plist->pid, killsig) != 0) {
312 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
313 exitcode = EXIT_FAILURE;
314 }
315 }
316 if (!(opt & OPT_SILENT)) {
317 printf("%u ", (unsigned)plist->pid);
318 }
319 }
320 plist = plist->next;
321 } while (plist);
322
323 if (!(opt & (OPT_SILENT))) {
324 bb_putchar('\n');
325 } 319 }
326 320
327 return exitcode; 321 return EXIT_FAILURE;
328} 322}
diff --git a/procps/iostat.c b/procps/iostat.c
index 06a33eb1d..cd233c72f 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -24,8 +24,6 @@
24#define debug(fmt, ...) ((void)0) 24#define debug(fmt, ...) ((void)0)
25 25
26#define MAX_DEVICE_NAME 12 26#define MAX_DEVICE_NAME 12
27#define CURRENT 0
28#define LAST 1
29 27
30#if 1 28#if 1
31typedef unsigned long long cputime_t; 29typedef unsigned long long cputime_t;
@@ -39,18 +37,33 @@ typedef long icputime_t;
39# define CPUTIME_MAX (~0UL) 37# define CPUTIME_MAX (~0UL)
40#endif 38#endif
41 39
42struct stats_cpu { 40enum {
43 cputime_t cpu_user; 41 STATS_CPU_USER,
44 cputime_t cpu_nice; 42 STATS_CPU_NICE,
45 cputime_t cpu_system; 43 STATS_CPU_SYSTEM,
46 cputime_t cpu_idle; 44 STATS_CPU_IDLE,
47 cputime_t cpu_iowait; 45 STATS_CPU_IOWAIT,
48 cputime_t cpu_steal; 46 STATS_CPU_IRQ,
49 cputime_t cpu_irq; 47 STATS_CPU_SOFTIRQ,
50 cputime_t cpu_softirq; 48 STATS_CPU_STEAL,
51 cputime_t cpu_guest; 49 STATS_CPU_GUEST,
50
51 GLOBAL_UPTIME,
52 SMP_UPTIME,
53
54 N_STATS_CPU,
52}; 55};
53 56
57typedef struct {
58 cputime_t vector[N_STATS_CPU];
59} stats_cpu_t;
60
61typedef struct {
62 stats_cpu_t *prev;
63 stats_cpu_t *curr;
64 cputime_t itv;
65} stats_cpu_pair_t;
66
54struct stats_dev { 67struct stats_dev {
55 char dname[MAX_DEVICE_NAME]; 68 char dname[MAX_DEVICE_NAME];
56 unsigned long long rd_sectors; 69 unsigned long long rd_sectors;
@@ -59,24 +72,24 @@ struct stats_dev {
59 unsigned long wr_ops; 72 unsigned long wr_ops;
60}; 73};
61 74
62/* List of devices entered on the command line */
63struct device_list {
64 char dname[MAX_DEVICE_NAME];
65};
66
67/* Globals. Sort by size and access frequency. */ 75/* Globals. Sort by size and access frequency. */
68struct globals { 76struct globals {
69 smallint show_all; 77 smallint show_all;
70 unsigned devlist_i; /* Index to the list of devices */
71 unsigned total_cpus; /* Number of CPUs */ 78 unsigned total_cpus; /* Number of CPUs */
72 unsigned clk_tck; /* Number of clock ticks per second */ 79 unsigned clk_tck; /* Number of clock ticks per second */
73 struct device_list *dlist; 80 llist_t *dev_list; /* List of devices entered on the command line */
74 struct stats_dev *saved_stats_dev; 81 struct stats_dev *saved_stats_dev;
75 struct tm tmtime; 82 struct tm tmtime;
83 struct {
84 const char *str;
85 unsigned div;
86 } unit;
76}; 87};
77#define G (*ptr_to_globals) 88#define G (*ptr_to_globals)
78#define INIT_G() do { \ 89#define INIT_G() do { \
79 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 90 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
91 G.unit.str = "Blk"; \
92 G.unit.div = 1; \
80} while (0) 93} while (0)
81 94
82/* Must match option string! */ 95/* Must match option string! */
@@ -104,12 +117,12 @@ static void print_header(void)
104 char buf[16]; 117 char buf[16];
105 struct utsname uts; 118 struct utsname uts;
106 119
107 if (uname(&uts) < 0) 120 uname(&uts); /* never fails */
108 bb_perror_msg_and_die("uname");
109 121
122 /* Date representation for the current locale */
110 strftime(buf, sizeof(buf), "%x", &G.tmtime); 123 strftime(buf, sizeof(buf), "%x", &G.tmtime);
111 124
112 printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n\n", 125 printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
113 uts.sysname, uts.release, uts.nodename, 126 uts.sysname, uts.release, uts.nodename,
114 buf, uts.machine, G.total_cpus); 127 buf, uts.machine, G.total_cpus);
115} 128}
@@ -124,61 +137,58 @@ static void get_localtime(struct tm *ptm)
124static void print_timestamp(void) 137static void print_timestamp(void)
125{ 138{
126 char buf[20]; 139 char buf[20];
140 /* %x: date representation for the current locale */
141 /* %X: time representation for the current locale */
127 strftime(buf, sizeof(buf), "%x %X", &G.tmtime); 142 strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
128 printf("%s\n", buf); 143 printf("%s\n", buf);
129} 144}
130 145
131/* Fetch CPU statistics from /proc/stat */ 146static cputime_t get_smp_uptime(void)
132static void get_cpu_statistics(struct stats_cpu *sc)
133{ 147{
134 FILE *fp; 148 FILE *fp;
135 char buf[1024]; 149 unsigned long sec, dec;
136
137 fp = xfopen_for_read("/proc/stat");
138 150
139 memset(sc, 0, sizeof(*sc)); 151 fp = xfopen_for_read("/proc/uptime");
140 152
141 while (fgets(buf, sizeof(buf), fp)) { 153 if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
142 /* Does the line starts with "cpu "? */ 154 bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
143 if (starts_with_cpu(buf) && buf[3] == ' ') {
144 sscanf(buf + 4 + 1,
145 "%"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"
146 FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
147 &sc->cpu_user, &sc->cpu_nice, &sc->cpu_system,
148 &sc->cpu_idle, &sc->cpu_iowait, &sc->cpu_irq,
149 &sc->cpu_softirq, &sc->cpu_steal, &sc->cpu_guest);
150 }
151 }
152 155
153 fclose(fp); 156 fclose(fp);
157
158 return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
154} 159}
155 160
156static cputime_t get_smp_uptime(void) 161/* Fetch CPU statistics from /proc/stat */
162static void get_cpu_statistics(stats_cpu_t *sc)
157{ 163{
158 FILE *fp; 164 FILE *fp;
159 char buf[sizeof(long)*3 * 2 + 4]; 165 char buf[1024], *ibuf = buf + 4;
160 unsigned long sec, dec;
161 166
162 fp = xfopen_for_read("/proc/uptime"); 167 fp = xfopen_for_read("/proc/stat");
163 168
164 if (fgets(buf, sizeof(buf), fp)) 169 memset(sc, 0, sizeof(*sc));
165 if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2)
166 bb_error_msg_and_die("can't read /proc/uptime");
167 170
168 fclose(fp); 171 while (fgets(buf, sizeof(buf), fp)) {
172 /* Does the line starts with "cpu "? */
173 if (!starts_with_cpu(buf) || buf[3] != ' ') {
174 continue;
175 }
176 for (int i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
177 ibuf = skip_whitespace(ibuf);
178 sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
179 if (i != STATS_CPU_GUEST) {
180 sc->vector[GLOBAL_UPTIME] += sc->vector[i];
181 }
182 ibuf = skip_non_whitespace(ibuf);
183 }
184 break;
185 }
169 186
170 return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; 187 if (this_is_smp()) {
171} 188 sc->vector[SMP_UPTIME] = get_smp_uptime();
189 }
172 190
173/* 191 fclose(fp);
174 * Obtain current uptime in jiffies.
175 * Uptime is sum of individual CPUs' uptimes.
176 */
177static cputime_t get_uptime(const struct stats_cpu *sc)
178{
179 /* NB: Don't include cpu_guest, it is already in cpu_user */
180 return sc->cpu_user + sc->cpu_nice + sc->cpu_system + sc->cpu_idle +
181 + sc->cpu_iowait + sc->cpu_irq + sc->cpu_steal + sc->cpu_softirq;
182} 192}
183 193
184static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new) 194static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
@@ -219,65 +229,51 @@ static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
219 return ((double)overflow_safe_sub(prev, curr)) / itv * 100; 229 return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
220} 230}
221 231
222static void print_stats_cpu_struct(const struct stats_cpu *p, 232static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
223 const struct stats_cpu *c, cputime_t itv)
224{ 233{
234 cputime_t *p = stats->prev->vector;
235 cputime_t *c = stats->curr->vector;
225 printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", 236 printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n",
226 percent_value(p->cpu_user , c->cpu_user , itv), 237 percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv),
227 percent_value(p->cpu_nice , c->cpu_nice , itv), 238 percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv),
228 percent_value(p->cpu_system + p->cpu_softirq + p->cpu_irq, 239 percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
229 c->cpu_system + c->cpu_softirq + c->cpu_irq, itv), 240 c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
230 percent_value(p->cpu_iowait , c->cpu_iowait , itv), 241 percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
231 percent_value(p->cpu_steal , c->cpu_steal , itv), 242 percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
232 percent_value(p->cpu_idle , c->cpu_idle , itv) 243 percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv)
233 ); 244 );
234} 245}
235 246
236static void print_stats_dev_struct(const struct stats_dev *p, 247static void print_stats_dev_struct(const struct stats_dev *p,
237 const struct stats_dev *c, cputime_t itv) 248 const struct stats_dev *c, cputime_t itv)
238{ 249{
239 int unit = 1;
240
241 if (option_mask32 & OPT_k)
242 unit = 2;
243 else if (option_mask32 & OPT_m)
244 unit = 2048;
245
246 if (option_mask32 & OPT_z) 250 if (option_mask32 & OPT_z)
247 if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops) 251 if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
248 return; 252 return;
249 253
250 printf("%-13s", c->dname); 254 printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu \n", c->dname,
251 printf(" %8.2f %12.2f %12.2f %10llu %10llu \n",
252 percent_value(p->rd_ops + p->wr_ops , 255 percent_value(p->rd_ops + p->wr_ops ,
253 /**/ c->rd_ops + c->wr_ops , itv), 256 /**/ c->rd_ops + c->wr_ops , itv),
254 percent_value(p->rd_sectors, c->rd_sectors, itv) / unit, 257 percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
255 percent_value(p->wr_sectors, c->wr_sectors, itv) / unit, 258 percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
256 (c->rd_sectors - p->rd_sectors) / unit, 259 (c->rd_sectors - p->rd_sectors) / G.unit.div,
257 (c->wr_sectors - p->wr_sectors) / unit); 260 (c->wr_sectors - p->wr_sectors) / G.unit.div);
258} 261}
259 262
260static void cpu_report(const struct stats_cpu *last, 263static void cpu_report(stats_cpu_pair_t *stats)
261 const struct stats_cpu *cur,
262 cputime_t itv)
263{ 264{
264 /* Always print a header */ 265 /* Always print a header */
265 puts("avg-cpu: %user %nice %system %iowait %steal %idle"); 266 puts("avg-cpu: %user %nice %system %iowait %steal %idle");
266 267
267 /* Print current statistics */ 268 /* Print current statistics */
268 print_stats_cpu_struct(last, cur, itv); 269 print_stats_cpu_struct(stats);
269} 270}
270 271
271static void print_devstat_header(void) 272static void print_devstat_header(void)
272{ 273{
273 printf("Device: tps"); 274 printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n", "tps",
274 275 G.unit.str, "_read", G.unit.str, "_wrtn",
275 if (option_mask32 & OPT_m) 276 G.unit.str, "_read", G.unit.str, "_wrtn");
276 puts(" MB_read/s MB_wrtn/s MB_read MB_wrtn");
277 else if (option_mask32 & OPT_k)
278 puts(" kB_read/s kB_wrtn/s kB_read kB_wrtn");
279 else
280 puts(" Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn");
281} 277}
282 278
283/* 279/*
@@ -289,37 +285,6 @@ static int is_partition(const char *dev)
289 return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]); 285 return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
290} 286}
291 287
292/*
293 * Return number of numbers on cmdline.
294 * Reasonable values are only 0 (no interval/count specified),
295 * 1 (interval specified) and 2 (both interval and count specified)
296 */
297static int numbers_on_cmdline(int argc, char *argv[])
298{
299 int sum = 0;
300
301 if (isdigit(argv[argc-1][0]))
302 sum++;
303 if (argc > 2 && isdigit(argv[argc-2][0]))
304 sum++;
305
306 return sum;
307}
308
309static int is_dev_in_dlist(const char *dev)
310{
311 int i;
312
313 /* Go through the device list */
314 for (i = 0; i < G.devlist_i; i++)
315 if (strcmp(G.dlist[i].dname, dev) == 0)
316 /* Found a match */
317 return 1;
318
319 /* No match found */
320 return 0;
321}
322
323static void do_disk_statistics(cputime_t itv) 288static void do_disk_statistics(cputime_t itv)
324{ 289{
325 FILE *fp; 290 FILE *fp;
@@ -356,7 +321,7 @@ static void do_disk_statistics(cputime_t itv)
356 break; 321 break;
357 } 322 }
358 323
359 if (!G.devlist_i && !is_partition(sd.dname)) { 324 if (!G.dev_list && !is_partition(sd.dname)) {
360 /* User didn't specify device */ 325 /* User didn't specify device */
361 if (!G.show_all && !sd.rd_ops && !sd.wr_ops) { 326 if (!G.show_all && !sd.rd_ops && !sd.wr_ops) {
362 /* Don't print unused device */ 327 /* Don't print unused device */
@@ -367,7 +332,7 @@ static void do_disk_statistics(cputime_t itv)
367 i++; 332 i++;
368 } else { 333 } else {
369 /* Is device in device list? */ 334 /* Is device in device list? */
370 if (is_dev_in_dlist(sd.dname)) { 335 if (llist_find_str(G.dev_list, sd.dname)) {
371 /* Print current statistics */ 336 /* Print current statistics */
372 print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv); 337 print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv);
373 G.saved_stats_dev[i] = sd; 338 G.saved_stats_dev[i] = sd;
@@ -389,28 +354,6 @@ static void dev_report(cputime_t itv)
389 do_disk_statistics(itv); 354 do_disk_statistics(itv);
390} 355}
391 356
392static void save_to_devlist(const char *dname)
393{
394 int i;
395 struct device_list *tmp = G.dlist;
396
397 if (strncmp(dname, "/dev/", 5) == 0)
398 /* We'll ignore prefix '/dev/' */
399 dname += 5;
400
401 /* Go through the list */
402 for (i = 0; i < G.devlist_i; i++, tmp++)
403 if (strcmp(tmp->dname, dname) == 0)
404 /* Already in the list */
405 return;
406
407 /* Add device name to the list */
408 strncpy(tmp->dname, dname, MAX_DEVICE_NAME - 1);
409
410 /* Update device list index */
411 G.devlist_i++;
412}
413
414static unsigned get_number_of_devices(void) 357static unsigned get_number_of_devices(void)
415{ 358{
416 FILE *fp; 359 FILE *fp;
@@ -440,18 +383,6 @@ static unsigned get_number_of_devices(void)
440 return n; 383 return n;
441} 384}
442 385
443static int number_of_ALL_on_cmdline(char **argv)
444{
445 int alls = 0;
446
447 /* Iterate over cmd line arguments, count "ALL" */
448 while (*argv)
449 if (strcmp(*argv++, "ALL") == 0)
450 alls++;
451
452 return alls;
453}
454
455//usage:#define iostat_trivial_usage 386//usage:#define iostat_trivial_usage
456//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]" 387//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
457//usage:#define iostat_full_usage "\n\n" 388//usage:#define iostat_full_usage "\n\n"
@@ -465,19 +396,17 @@ static int number_of_ALL_on_cmdline(char **argv)
465//usage: "\n -m Use Mb/s" 396//usage: "\n -m Use Mb/s"
466 397
467int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 398int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
468int iostat_main(int argc, char **argv) 399int iostat_main(int argc UNUSED_PARAM, char **argv)
469{ 400{
470 int opt, dev_num; 401 int opt, dev_num;
471 unsigned interval = 0; 402 unsigned interval;
472 int count; 403 int count;
473 cputime_t global_uptime[2] = { 0 }; 404 stats_cpu_t stats_data[2];
474 cputime_t smp_uptime[2] = { 0 }; 405 smallint current_stats;
475 cputime_t itv;
476 struct stats_cpu stats_cur, stats_last;
477 406
478 INIT_G(); 407 INIT_G();
479 408
480 memset(&stats_last, 0, sizeof(stats_last)); 409 memset(&stats_data, 0, sizeof(stats_data));
481 410
482 /* Get number of clock ticks per sec */ 411 /* Get number of clock ticks per sec */
483 G.clk_tck = get_user_hz(); 412 G.clk_tck = get_user_hz();
@@ -496,25 +425,24 @@ int iostat_main(int argc, char **argv)
496 opt |= OPT_c + OPT_d; 425 opt |= OPT_c + OPT_d;
497 426
498 argv += optind; 427 argv += optind;
499 argc -= optind;
500
501 dev_num = argc - numbers_on_cmdline(argc, argv);
502 /* We don't want to allocate space for 'ALL' */
503 dev_num -= number_of_ALL_on_cmdline(argv);
504 if (dev_num > 0)
505 /* Make space for device list */
506 G.dlist = xzalloc(sizeof(G.dlist[0]) * dev_num);
507 428
508 /* Store device names into device list */ 429 /* Store device names into device list */
430 dev_num = 0;
509 while (*argv && !isdigit(*argv[0])) { 431 while (*argv && !isdigit(*argv[0])) {
510 if (strcmp(*argv, "ALL") != 0) 432 if (strcmp(*argv, "ALL") != 0) {
511 /* If not ALL, save device name */ 433 /* If not ALL, save device name */
512 save_to_devlist(*argv); 434 char *dev_name = skip_dev_pfx(*argv);
513 else 435 if (!llist_find_str(G.dev_list, dev_name)) {
436 llist_add_to(&G.dev_list, dev_name);
437 dev_num++;
438 }
439 } else {
514 G.show_all = 1; 440 G.show_all = 1;
441 }
515 argv++; 442 argv++;
516 } 443 }
517 444
445 interval = 0;
518 count = 1; 446 count = 1;
519 if (*argv) { 447 if (*argv) {
520 /* Get interval */ 448 /* Get interval */
@@ -533,28 +461,46 @@ int iostat_main(int argc, char **argv)
533 ); 461 );
534 } 462 }
535 463
464 if (opt & OPT_m) {
465 G.unit.str = " MB";
466 G.unit.div = 2048;
467 }
468
469 if (opt & OPT_k) {
470 G.unit.str = " kB";
471 G.unit.div = 2;
472 }
473
474 get_localtime(&G.tmtime);
475
536 /* Display header */ 476 /* Display header */
537 print_header(); 477 print_header();
538 478
479 current_stats = 0;
539 /* Main loop */ 480 /* Main loop */
540 for (;;) { 481 for (;;) {
482 stats_cpu_pair_t stats;
483
484 stats.prev = &stats_data[current_stats ^ 1];
485 stats.curr = &stats_data[current_stats];
486
541 /* Fill the time structure */ 487 /* Fill the time structure */
542 get_localtime(&G.tmtime); 488 get_localtime(&G.tmtime);
543 489
544 /* Fetch current CPU statistics */ 490 /* Fetch current CPU statistics */
545 get_cpu_statistics(&stats_cur); 491 get_cpu_statistics(stats.curr);
546
547 /* Fetch current uptime */
548 global_uptime[CURRENT] = get_uptime(&stats_cur);
549 492
550 /* Get interval */ 493 /* Get interval */
551 itv = get_interval(global_uptime[LAST], global_uptime[CURRENT]); 494 stats.itv = get_interval(
495 stats.prev->vector[GLOBAL_UPTIME],
496 stats.curr->vector[GLOBAL_UPTIME]
497 );
552 498
553 if (opt & OPT_t) 499 if (opt & OPT_t)
554 print_timestamp(); 500 print_timestamp();
555 501
556 if (opt & OPT_c) { 502 if (opt & OPT_c) {
557 cpu_report(&stats_last, &stats_cur, itv); 503 cpu_report(&stats);
558 if (opt & OPT_d) 504 if (opt & OPT_d)
559 /* Separate outputs by a newline */ 505 /* Separate outputs by a newline */
560 bb_putchar('\n'); 506 bb_putchar('\n');
@@ -562,32 +508,31 @@ int iostat_main(int argc, char **argv)
562 508
563 if (opt & OPT_d) { 509 if (opt & OPT_d) {
564 if (this_is_smp()) { 510 if (this_is_smp()) {
565 smp_uptime[CURRENT] = get_smp_uptime(); 511 stats.itv = get_interval(
566 itv = get_interval(smp_uptime[LAST], smp_uptime[CURRENT]); 512 stats.prev->vector[SMP_UPTIME],
567 smp_uptime[LAST] = smp_uptime[CURRENT]; 513 stats.curr->vector[SMP_UPTIME]
514 );
568 } 515 }
569 dev_report(itv); 516 dev_report(stats.itv);
570 } 517 }
571 518
519 bb_putchar('\n');
520
572 if (count > 0) { 521 if (count > 0) {
573 if (--count == 0) 522 if (--count == 0)
574 break; 523 break;
575 } 524 }
576 525
577 /* Backup current stats */ 526 /* Swap stats */
578 global_uptime[LAST] = global_uptime[CURRENT]; 527 current_stats ^= 1;
579 stats_last = stats_cur;
580 528
581 bb_putchar('\n');
582 sleep(interval); 529 sleep(interval);
583 } 530 }
584 531
585 bb_putchar('\n');
586
587 if (ENABLE_FEATURE_CLEAN_UP) { 532 if (ENABLE_FEATURE_CLEAN_UP) {
588 free(&G); 533 llist_free(G.dev_list, NULL);
589 free(G.dlist);
590 free(G.saved_stats_dev); 534 free(G.saved_stats_dev);
535 free(&G);
591 } 536 }
592 537
593 return EXIT_SUCCESS; 538 return EXIT_SUCCESS;
diff --git a/procps/mpstat.c b/procps/mpstat.c
index d643c999f..da8f34dab 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -36,11 +36,10 @@
36 * We are printing headers in the " IRQNAME/s" form, experimentally 36 * We are printing headers in the " IRQNAME/s" form, experimentally
37 * anything smaller than 10 chars looks ugly for /proc/softirqs stats. 37 * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
38 */ 38 */
39#define INTRATE_SCRWIDTH 10 39#define INTRATE_SCRWIDTH 10
40#define INTRATE_SCRWIDTH_STR "10" 40#define INTRATE_SCRWIDTH_STR "10"
41 41
42/* System files */ 42/* System files */
43#define SYSFS_DEVCPU "/sys/devices/system/cpu"
44#define PROCFS_STAT "/proc/stat" 43#define PROCFS_STAT "/proc/stat"
45#define PROCFS_INTERRUPTS "/proc/interrupts" 44#define PROCFS_INTERRUPTS "/proc/interrupts"
46#define PROCFS_SOFTIRQS "/proc/softirqs" 45#define PROCFS_SOFTIRQS "/proc/softirqs"
diff --git a/procps/nmeter.c b/procps/nmeter.c
index 48b56a399..999955982 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -24,20 +24,20 @@
24//usage: "\n -d MSEC Milliseconds between updates (default:1000)" 24//usage: "\n -d MSEC Milliseconds between updates (default:1000)"
25//usage: "\n" 25//usage: "\n"
26//usage: "\nFormat specifiers:" 26//usage: "\nFormat specifiers:"
27//usage: "\n %Nc or %[cN] Monitor CPU. N - bar size (default:10)" 27//usage: "\n %Nc or %[cN] CPU. N - bar size (default:10)"
28//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)" 28//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
29//usage: "\n %[niface] Monitor network interface 'iface'" 29//usage: "\n %[nINTERFACE] Network INTERFACE"
30//usage: "\n %m Monitor allocated memory" 30//usage: "\n %m Allocated memory"
31//usage: "\n %[mf] Monitor free memory" 31//usage: "\n %[mf] Free memory"
32//usage: "\n %[mt] Monitor total memory" 32//usage: "\n %[mt] Total memory"
33//usage: "\n %s Monitor allocated swap" 33//usage: "\n %s Allocated swap"
34//usage: "\n %f Monitor number of used file descriptors" 34//usage: "\n %f Number of used file descriptors"
35//usage: "\n %Ni Monitor total/specific IRQ rate" 35//usage: "\n %Ni Total/specific IRQ rate"
36//usage: "\n %x Monitor context switch rate" 36//usage: "\n %x Context switch rate"
37//usage: "\n %p Monitor forks" 37//usage: "\n %p Forks"
38//usage: "\n %[pn] Monitor # of processes" 38//usage: "\n %[pn] # of processes"
39//usage: "\n %b Monitor block io" 39//usage: "\n %b Block io"
40//usage: "\n %Nt Show time (with N decimal points)" 40//usage: "\n %Nt Time (with N decimal points)"
41//usage: "\n %r Print <cr> instead of <lf> at EOL" 41//usage: "\n %r Print <cr> instead of <lf> at EOL"
42 42
43//TODO: 43//TODO:
diff --git a/procps/powertop.c b/procps/powertop.c
index bfe5a9568..008cdfca4 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -393,11 +393,9 @@ static NOINLINE int process_timer_stats(void)
393 char buf[128]; 393 char buf[128];
394 char line[15 + 3 + 128]; 394 char line[15 + 3 + 128];
395 int n; 395 int n;
396 ullong totalticks;
397 FILE *fp; 396 FILE *fp;
398 397
399 buf[0] = '\0'; 398 buf[0] = '\0';
400 totalticks = 0;
401 399
402 n = 0; 400 n = 0;
403 fp = NULL; 401 fp = NULL;
diff --git a/procps/top.c b/procps/top.c
index ee6555188..011bbf183 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -92,9 +92,9 @@ enum { SORT_DEPTH = 3 };
92struct globals { 92struct globals {
93 top_status_t *top; 93 top_status_t *top;
94 int ntop; 94 int ntop;
95 smallint inverted;
95#if ENABLE_FEATURE_TOPMEM 96#if ENABLE_FEATURE_TOPMEM
96 smallint sort_field; 97 smallint sort_field;
97 smallint inverted;
98#endif 98#endif
99#if ENABLE_FEATURE_TOP_SMP_CPU 99#if ENABLE_FEATURE_TOP_SMP_CPU
100 smallint smp_cpu_info; /* one/many cpu info lines? */ 100 smallint smp_cpu_info; /* one/many cpu info lines? */
@@ -194,9 +194,9 @@ static int mult_lvl_cmp(void* a, void* b)
194 for (i = 0; i < SORT_DEPTH; i++) { 194 for (i = 0; i < SORT_DEPTH; i++) {
195 cmp_val = (*sort_function[i])(a, b); 195 cmp_val = (*sort_function[i])(a, b);
196 if (cmp_val != 0) 196 if (cmp_val != 0)
197 return cmp_val; 197 break;
198 } 198 }
199 return 0; 199 return inverted ? -cmp_val : cmp_val;
200} 200}
201 201
202static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) 202static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
@@ -797,7 +797,7 @@ static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
797 797
798 display_topmem_header(scr_width, &lines_rem); 798 display_topmem_header(scr_width, &lines_rem);
799 strcpy(line_buf, HDR_STR " COMMAND"); 799 strcpy(line_buf, HDR_STR " COMMAND");
800 line_buf[5 + sort_field * 6] = '*'; 800 line_buf[11 + sort_field * 6] = "^_"[inverted];
801 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); 801 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
802 lines_rem--; 802 lines_rem--;
803 803
@@ -850,8 +850,114 @@ enum {
850 | PSSCAN_PID 850 | PSSCAN_PID
851 | PSSCAN_SMAPS 851 | PSSCAN_SMAPS
852 | PSSCAN_COMM, 852 | PSSCAN_COMM,
853 EXIT_MASK = (unsigned)-1,
853}; 854};
854 855
856#if ENABLE_FEATURE_USE_TERMIOS
857static unsigned handle_input(unsigned scan_mask, unsigned interval)
858{
859 unsigned char c;
860 struct pollfd pfd[1];
861
862 pfd[0].fd = 0;
863 pfd[0].events = POLLIN;
864
865 while (1) {
866 if (safe_poll(pfd, 1, interval * 1000) <= 0)
867 return scan_mask;
868 interval = 0;
869
870 if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
871 option_mask32 |= OPT_EOF;
872 return scan_mask;
873 }
874
875 if (c == initial_settings.c_cc[VINTR])
876 return EXIT_MASK;
877 if (c == initial_settings.c_cc[VEOF])
878 return EXIT_MASK;
879 c |= 0x20; /* lowercase */
880 if (c == 'q')
881 return EXIT_MASK;
882
883 if (c == 'n') {
884 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
885 sort_function[0] = pid_sort;
886 continue;
887 }
888 if (c == 'm') {
889 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
890 sort_function[0] = mem_sort;
891# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
892 sort_function[1] = pcpu_sort;
893 sort_function[2] = time_sort;
894# endif
895 continue;
896 }
897# if ENABLE_FEATURE_SHOW_THREADS
898 if (c == 'h'
899 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
900 ) {
901 scan_mask ^= PSSCAN_TASKS;
902 continue;
903 }
904# endif
905# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
906 if (c == 'p') {
907 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
908 sort_function[0] = pcpu_sort;
909 sort_function[1] = mem_sort;
910 sort_function[2] = time_sort;
911 continue;
912 }
913 if (c == 't') {
914 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
915 sort_function[0] = time_sort;
916 sort_function[1] = mem_sort;
917 sort_function[2] = pcpu_sort;
918 continue;
919 }
920# if ENABLE_FEATURE_TOPMEM
921 if (c == 's') {
922 scan_mask = TOPMEM_MASK;
923 free(prev_hist);
924 prev_hist = NULL;
925 prev_hist_count = 0;
926 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
927 continue;
928 }
929# endif
930 if (c == 'r') {
931 inverted ^= 1;
932 continue;
933 }
934# if ENABLE_FEATURE_TOP_SMP_CPU
935 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
936 if (c == 'c' || c == '1') {
937 /* User wants to toggle per cpu <> aggregate */
938 if (smp_cpu_info) {
939 free(cpu_prev_jif);
940 free(cpu_jif);
941 cpu_jif = &cur_jif;
942 cpu_prev_jif = &prev_jif;
943 } else {
944 /* Prepare for xrealloc() */
945 cpu_jif = cpu_prev_jif = NULL;
946 }
947 num_cpus = 0;
948 smp_cpu_info = !smp_cpu_info;
949 get_jiffy_counts();
950 continue;
951 }
952# endif
953# endif
954 break; /* unknown key -> force refresh */
955 }
956
957 return scan_mask;
958}
959#endif
960
855//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU 961//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
856//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ 962//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
857//usage:#else 963//usage:#else
@@ -871,8 +977,9 @@ enum {
871//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") 977//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
872//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") 978//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
873//usage: IF_FEATURE_TOPMEM( 979//usage: IF_FEATURE_TOPMEM(
874//usage: "\n"" S: show memory, R: reverse memory sort" 980//usage: "\n"" S: show memory"
875//usage: ) 981//usage: )
982//usage: "\n"" R: reverse sort"
876//usage: IF_SHOW_THREADS_OR_TOP_SMP( 983//usage: IF_SHOW_THREADS_OR_TOP_SMP(
877//usage: "\n"" " 984//usage: "\n"" "
878//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") 985//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads")
@@ -880,23 +987,36 @@ enum {
880//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") 987//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
881//usage: ) 988//usage: )
882//usage: "\n"" Q,^C: exit" 989//usage: "\n"" Q,^C: exit"
990//usage: "\n"
991//usage: "\n""Options:"
992//usage: "\n"" -b Batch mode"
993//usage: "\n"" -n N Exit after N iterations"
994//usage: "\n"" -d N Delay between updates"
995//usage: IF_FEATURE_TOPMEM(
996//usage: "\n"" -m Same as 's' key"
997//usage: )
998
999/* Interactive testing:
1000 * echo sss | ./busybox top
1001 * - shows memory screen
1002 * echo sss | ./busybox top -bn1 >mem
1003 * - saves memory screen - the *whole* list, not first NROWS processes!
1004 * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
1005 * - saves several different screens, and exits
1006 *
1007 * TODO: -i STRING param as a better alternative?
1008 */
883 1009
884int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1010int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
885int top_main(int argc UNUSED_PARAM, char **argv) 1011int top_main(int argc UNUSED_PARAM, char **argv)
886{ 1012{
887 int iterations; 1013 int iterations;
888 unsigned lines, col; 1014 unsigned lines, col;
889 int lines_rem;
890 unsigned interval; 1015 unsigned interval;
891 char *str_interval, *str_iterations; 1016 char *str_interval, *str_iterations;
892 unsigned scan_mask = TOP_MASK; 1017 unsigned scan_mask = TOP_MASK;
893#if ENABLE_FEATURE_USE_TERMIOS 1018#if ENABLE_FEATURE_USE_TERMIOS
894 struct termios new_settings; 1019 struct termios new_settings;
895 struct pollfd pfd[1];
896 unsigned char c;
897
898 pfd[0].fd = 0;
899 pfd[0].events = POLLIN;
900#endif 1020#endif
901 1021
902 INIT_G(); 1022 INIT_G();
@@ -933,15 +1053,6 @@ int top_main(int argc UNUSED_PARAM, char **argv)
933 1053
934 /* change to /proc */ 1054 /* change to /proc */
935 xchdir("/proc"); 1055 xchdir("/proc");
936#if ENABLE_FEATURE_USE_TERMIOS
937 tcgetattr(0, (void *) &initial_settings);
938 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
939 /* unbuffered input, turn off echo */
940 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
941
942 bb_signals(BB_FATAL_SIGS, sig_catcher);
943 tcsetattr_stdin_TCSANOW(&new_settings);
944#endif
945 1056
946#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1057#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
947 sort_function[0] = pcpu_sort; 1058 sort_function[0] = pcpu_sort;
@@ -951,21 +1062,41 @@ int top_main(int argc UNUSED_PARAM, char **argv)
951 sort_function[0] = mem_sort; 1062 sort_function[0] = mem_sort;
952#endif 1063#endif
953 1064
954 while (1) { 1065#if ENABLE_FEATURE_USE_TERMIOS
1066 tcgetattr(0, (void *) &initial_settings);
1067 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
1068 if (!OPT_BATCH_MODE) {
1069 /* unbuffered input, turn off echo */
1070 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
1071 tcsetattr_stdin_TCSANOW(&new_settings);
1072 }
1073
1074 bb_signals(BB_FATAL_SIGS, sig_catcher);
1075
1076 /* Eat initial input, if any */
1077 scan_mask = handle_input(scan_mask, 0);
1078#endif
1079
1080 while (scan_mask != EXIT_MASK) {
955 procps_status_t *p = NULL; 1081 procps_status_t *p = NULL;
956 1082
957 lines = 24; /* default */ 1083 if (OPT_BATCH_MODE) {
958 col = 79; 1084 lines = INT_MAX;
1085 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1086 } else {
1087 lines = 24; /* default */
1088 col = 79;
959#if ENABLE_FEATURE_USE_TERMIOS 1089#if ENABLE_FEATURE_USE_TERMIOS
960 /* We output to stdout, we need size of stdout (not stdin)! */ 1090 /* We output to stdout, we need size of stdout (not stdin)! */
961 get_terminal_width_height(STDOUT_FILENO, &col, &lines); 1091 get_terminal_width_height(STDOUT_FILENO, &col, &lines);
962 if (lines < 5 || col < 10) { 1092 if (lines < 5 || col < 10) {
963 sleep(interval); 1093 sleep(interval);
964 continue; 1094 continue;
965 } 1095 }
966#endif 1096#endif
967 if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ 1097 if (col > LINE_BUF_SIZE - 2)
968 col = LINE_BUF_SIZE-2; 1098 col = LINE_BUF_SIZE - 2;
1099 }
969 1100
970 /* read process IDs & status for all the processes */ 1101 /* read process IDs & status for all the processes */
971 while ((p = procps_scan(p, scan_mask)) != NULL) { 1102 while ((p = procps_scan(p, scan_mask)) != NULL) {
@@ -1033,15 +1164,11 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1033 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1164 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1034 } 1165 }
1035#endif 1166#endif
1036 lines_rem = lines;
1037 if (OPT_BATCH_MODE) {
1038 lines_rem = INT_MAX;
1039 }
1040 if (scan_mask != TOPMEM_MASK) 1167 if (scan_mask != TOPMEM_MASK)
1041 display_process_list(lines_rem, col); 1168 display_process_list(lines, col);
1042#if ENABLE_FEATURE_TOPMEM 1169#if ENABLE_FEATURE_TOPMEM
1043 else 1170 else
1044 display_topmem_process_list(lines_rem, col); 1171 display_topmem_process_list(lines, col);
1045#endif 1172#endif
1046 clearmems(); 1173 clearmems();
1047 if (iterations >= 0 && !--iterations) 1174 if (iterations >= 0 && !--iterations)
@@ -1049,84 +1176,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1049#if !ENABLE_FEATURE_USE_TERMIOS 1176#if !ENABLE_FEATURE_USE_TERMIOS
1050 sleep(interval); 1177 sleep(interval);
1051#else 1178#else
1052 if (option_mask32 & (OPT_b|OPT_EOF)) 1179 if (option_mask32 & OPT_EOF)
1053 /* batch mode, or EOF on stdin ("top </dev/null") */ 1180 /* EOF on stdin ("top </dev/null") */
1054 sleep(interval); 1181 sleep(interval);
1055 else if (safe_poll(pfd, 1, interval * 1000) > 0) { 1182 else
1056 if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ 1183 scan_mask = handle_input(scan_mask, interval);
1057 option_mask32 |= OPT_EOF;
1058 continue;
1059 }
1060 if (c == initial_settings.c_cc[VINTR])
1061 break;
1062 c |= 0x20; /* lowercase */
1063 if (c == 'q')
1064 break;
1065 if (c == 'n') {
1066 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1067 sort_function[0] = pid_sort;
1068 }
1069 if (c == 'm') {
1070 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1071 sort_function[0] = mem_sort;
1072# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1073 sort_function[1] = pcpu_sort;
1074 sort_function[2] = time_sort;
1075# endif
1076 }
1077# if ENABLE_FEATURE_SHOW_THREADS
1078 if (c == 'h'
1079 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
1080 ) {
1081 scan_mask ^= PSSCAN_TASKS;
1082 }
1083# endif
1084# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1085 if (c == 'p') {
1086 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1087 sort_function[0] = pcpu_sort;
1088 sort_function[1] = mem_sort;
1089 sort_function[2] = time_sort;
1090 }
1091 if (c == 't') {
1092 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1093 sort_function[0] = time_sort;
1094 sort_function[1] = mem_sort;
1095 sort_function[2] = pcpu_sort;
1096 }
1097# if ENABLE_FEATURE_TOPMEM
1098 if (c == 's') {
1099 scan_mask = TOPMEM_MASK;
1100 free(prev_hist);
1101 prev_hist = NULL;
1102 prev_hist_count = 0;
1103 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1104 }
1105 if (c == 'r')
1106 inverted ^= 1;
1107# endif
1108# if ENABLE_FEATURE_TOP_SMP_CPU
1109 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1110 if (c == 'c' || c == '1') {
1111 /* User wants to toggle per cpu <> aggregate */
1112 if (smp_cpu_info) {
1113 free(cpu_prev_jif);
1114 free(cpu_jif);
1115 cpu_jif = &cur_jif;
1116 cpu_prev_jif = &prev_jif;
1117 } else {
1118 /* Prepare for xrealloc() */
1119 cpu_jif = cpu_prev_jif = NULL;
1120 }
1121 num_cpus = 0;
1122 smp_cpu_info = !smp_cpu_info;
1123 get_jiffy_counts();
1124 }
1125# endif
1126# endif
1127 }
1128#endif /* FEATURE_USE_TERMIOS */ 1184#endif /* FEATURE_USE_TERMIOS */
1129 } /* end of "while (1)" */ 1185 } /* end of "while (not Q)" */
1130 1186
1131 bb_putchar('\n'); 1187 bb_putchar('\n');
1132#if ENABLE_FEATURE_USE_TERMIOS 1188#if ENABLE_FEATURE_USE_TERMIOS