1 /* ps.c - show process list
2 *
3 * Copyright 2015 Rob Landley <[email protected]>
4 *
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
6 * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
7 * And linux kernel source fs/proc/array.c function do_task_stat()
8 *
9 * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to
10 * mean "show numeric users and groups" instead.
11 * Posix says default output should have field named "TTY" but if you "-o tty"
12 * the same field should be called "TT" which is _INSANE_ and I'm not doing it.
13 * Similarly -f outputs USER but calls it UID (we call it USER).
14 * It also says that -o "args" and "comm" should behave differently but use
15 * the same title, which is not the same title as the default output. (No.)
16 * Select by session id is -s not -g. Posix doesn't say truncated fields
17 * should end with "+" but it's pretty common behavior.
18 *
19 * Posix defines -o ADDR as "The address of the process" but the process
20 * start address is a constant on any elf system with mmu. The procps ADDR
21 * field always prints "-" with an alignment of 1, which is why it has 11
22 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
23 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
24 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
25 * which changes -l by removing the "F" column and swapping RSS for ADDR,
26 * leaving 9 chars for cmd, so we're using that as our -l output.
27 *
28 * Added a bunch of new -o fields posix doesn't mention, and we don't
29 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
30 * output argv[0] unmodified for -o comm or -o args (but procps violates
31 * posix for -o comm anyway, it's stat[2] not argv[0]).
32 *
33 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
34 * files (why they're not globally readable when the rest of proc
35 * data is...?) and get a global I/O picture. Normal top is NOT,
36 * even though you can -o AIO there, to give sysadmins the option
37 * to reduce security exposure.)
38 *
39 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
40 * TODO: switch -fl to -y
41 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
42 * TODO: iotop: Window size change: respond immediately. Why not padding
43 * at right edge? (Not adjusting to screen size at all? Header wraps?)
44 * TODO: top: thread support and SMP
45 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
46 * TODO: pgrep qemu-system-i386 never matches because one char too long
47
48 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN))
49 // stayroot because iotop needs root to read other process' proc/$$/io
50 // TOP and IOTOP have a large common option block used for common processing,
51 // the default values are different but the flags are in the same order.
52 USE_TOP(NEWTOY(top, ">0O*h" "Hk*o*p*u*s#<1d%<100=3000m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN))
53 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d%<100=3000m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
54 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
55 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
56
57 config PS
58 bool "ps"
59 default y
60 help
61 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
62
63 List processes.
64
65 Which processes to show (-gGuUpPt selections may be comma separated lists):
66
67 -A All -a Has terminal not session leader
68 -d All but session leaders -e Synonym for -A
69 -g In GROUPs -G In real GROUPs (before sgid)
70 -p PIDs (--pid) -P Parent PIDs (--ppid)
71 -s In session IDs -t Attached to selected TTYs
72 -T Show threads also -u Owned by selected USERs
73 -U Real USERs (before suid)
74
75 Output modifiers:
76
77 -k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths
78 -n Show numeric USER and GROUP -w Wide output (don't truncate fields)
79
80 Which FIELDs to show. (-o HELP for list, default = -o PID,TTY,TIME,CMD)
81
82 -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
83 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
84 -o Output FIELDs instead of defaults, each with optional :size and =title
85 -O Add FIELDS to defaults
86 -Z Include LABEL
87
88 config TOP
89 bool "top"
90 default y
91 help
92 usage: top [-Hhbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]
93
94 Show process activity in real time.
95
96 -H Show threads
97 -h Usage graphs instead of text
98 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
99 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
100 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
101 -s Sort by field number (1-X, default 9)
102 -b Batch mode (no tty)
103 -d Delay SECONDS between each cycle (default 3)
104 -m Maximum number of tasks to show
105 -n Exit after NUMBER iterations
106 -p Show these PIDs
107 -u Show these USERs
108 -q Quiet (no header lines)
109
110 Cursor UP/DOWN or LEFT/RIGHT to move list, SHIFT LEFT/RIGHT to change sort,
111 space to force update, R to reverse sort, Q to exit.
112
113 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
114 config IOTOP
115 bool "iotop"
116 default y
117 help
118 usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
119
120 Rank processes by I/O.
121
122 -A All I/O, not just disk
123 -a Accumulated I/O (not percentage)
124 -H Show threads
125 -K Kilobytes
126 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
127 -m Maximum number of tasks to show
128 -O Only show processes doing I/O
129 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
130 -s Sort by field number (0-X, default 6)
131 -b Batch mode (no tty)
132 -d Delay SECONDS between each cycle (default 3)
133 -n Exit after NUMBER iterations
134 -p Show these PIDs
135 -u Show these USERs
136 -q Quiet (no header lines)
137
138 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
139 update, R to reverse sort, Q to exit.
140
141 config PGREP
142 bool "pgrep"
143 default y
144 help
145 usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
146
147 Search for process(es). PATTERN is an extended regular expression checked
148 against command names.
149
150 -c Show only count of matches
151 -d Use DELIM instead of newline
152 -L Send SIGNAL instead of printing name
153 -l Show command name
154 -f Check full command line for PATTERN
155 -G Match real Group ID(s)
156 -g Match Process Group(s) (0 is current user)
157 -n Newest match only
158 -o Oldest match only
159 -P Match Parent Process ID(s)
160 -s Match Session ID(s) (0 for current)
161 -t Match Terminal(s)
162 -U Match real User ID(s)
163 -u Match effective User ID(s)
164 -v Negate the match
165 -x Match whole command (not substring)
166
167 config PKILL
168 bool "pkill"
169 default y
170 help
171 usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
172
173 -l Send SIGNAL (default SIGTERM)
174 -V Verbose
175 -f Check full command line for PATTERN
176 -G Match real Group ID(s)
177 -g Match Process Group(s) (0 is current user)
178 -n Newest match only
179 -o Oldest match only
180 -P Match Parent Process ID(s)
181 -s Match Session ID(s) (0 for current)
182 -t Match Terminal(s)
183 -U Match real User ID(s)
184 -u Match effective User ID(s)
185 -v Negate the match
186 -x Match whole command (not substring)
187 */
188
189 #define FOR_ps
190 #include "toys.h"
191
192 GLOBALS(
193 union {
194 struct {
195 struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k;
196 } ps;
197 struct {
198 long n, m, d, s;
199 struct arg_list *u, *p, *o, *k, *O;
200 } top;
201 struct {
202 char *L;
203 struct arg_list *G, *g, *P, *s, *t, *U, *u;
204 char *d;
205
206 void *regexes, *snapshot;
207 int signal;
208 pid_t self, match;
209 } pgrep;
210 };
211
212 struct ps_ptr_len {
213 void *ptr;
214 long len;
215 } gg, GG, pp, PP, ss, tt, uu, UU;
216 struct dirtree *threadparent;
217 unsigned width, height, scroll;
218 dev_t tty;
219 void *fields, *kfields;
220 long long ticks, bits, time;
221 int kcount, forcek, sortpos, pidlen;
222 int (*match_process)(long long *slot);
223 void (*show_process)(void *tb);
224 )
225
226 // Linked list of -o fields selected for display, in order, with :len and =title
227
228 struct ofields {
229 struct ofields *next, *prev;
230 short which, len, reverse;
231 char *title;
232 };
233
234 /* The function get_ps() reads all the data about one process, saving it in
235 * toybox as a struct procpid. Simple ps calls then pass toybuf directly to
236 * show_ps(), but features like sorting append a copy to a linked list
237 * for further processing once all processes have been read.
238 *
239 * struct procpid contains a slot[] array of 64 bit values, with the following
240 * data at each position in the array. Most is read from /proc/$PID/stat (see
241 * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but
242 * we replace several fields with don't use with other data. */
243
244 enum {
245 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
246 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
247 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
248 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
249 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
250 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
251 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime
252 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
253 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
254 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
255 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
256 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
257 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
258 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
259 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
260 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
261 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
262 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
263 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
264 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
265 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
266 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
267 // end of /proc/$PID/stat fields
268 SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length
269 SLOT_uptime, /*sysinfo.uptime*/ SLOT_totalram, // sysinfo.totalram
270 SLOT_vsz, /*Virtual mem Size*/ SLOT_shr, // Shared memory
271 SLOT_pcy, /*Android sched pol*/ SLOT_rchar, // All bytes read
272 SLOT_wchar, /*All bytes written*/ SLOT_rbytes, // Disk bytes read
273 SLOT_wbytes, /*Disk bytes written*/ SLOT_swap, // Swap pages used
274 SLOT_bits, /*32 or 64*/ SLOT_tid, // Thread ID
275 SLOT_tcount, /*Thread count*/
276
277 SLOT_count /* Size of array */
278 };
279
280 /* In addition to slot[], carevup contains 6 string fields to display
281 command name, tty device, selinux label... They're stored one after the
282 other in str[] (separated by null terminators), and offset[] contains the
283 starting position of each string after the first (which is always 0). */
284
285 // Data layout in toybuf
286 struct procpid {
287 long long slot[SLOT_count]; // data (see enum above)
288 unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0)
289 char state;
290 char pcy[3]; // Android scheduling policy
291 char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME
292 };
293
294 /* The typos[] array lists all the types understood by "ps -o", I.E all the
295 * columns ps and top know how to display. Each entry has:
296 *
297 * name: the column name, displayed at top and used to select column with -o
298 *
299 * width: the display width. Fields are padded to this width when displaying
300 * to a terminal (negative means right justified). Strings are truncated
301 * to fit, numerical fields are padded but not truncated (although
302 * the display code reclaims unused padding from later fields to try to
303 * get the overflow back).
304 *
305 * slot: which slot[] out of procpid. Negative means it's a string field.
306 * value|XX requests extra display/sort processing.
307 *
308 * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the
309 * first string argument and the prefix is the first argument to TAGGED_ARRAY
310 * so in this case "NAME" becomes PS_NAME which is the offset into typos[]
311 * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME).
312 * We record active columns in TT.bits, ala:
313 *
314 * if (TT.bits & _PS_NAME) printf("-o included PS_NAME");
315 */
316
317 #define XX 64 // force string representation for sorting, etc
318
319 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
320 struct typography {
321 char *name, *help;
322 signed char width, slot;
323 } static const typos[] = TAGGED_ARRAY(PS,
324 // Numbers. (What's in slot[] is what's displayed, sorted numerically.)
325 {"PID", "Process ID", 6, SLOT_pid},
326 {"PPID", "Parent Process ID", 6, SLOT_ppid},
327 {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority},
328 {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice},
329 {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip},
330 {"SZ", "4k pages to swap out", 5, SLOT_vsize},
331 {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss},
332 {"PGID", "Process Group ID", 5, SLOT_pgrp},
333 {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize},
334 {"MAJFL", "Major page faults", 6, SLOT_majflt},
335 {"MINFL", "Minor page faults", 6, SLOT_minflt},
336 {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority},
337 {"PSR", "Processor last executed on", 3, SLOT_taskcpu},
338 {"RTPRIO", "Realtime priority", 6, SLOT_rtprio},
339 {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)",
340 3, SLOT_policy},
341 {"CPU", "Which processor running on", 3, SLOT_taskcpu},
342 {"TID", "Thread ID", 5, SLOT_tid},
343 {"TCNT", "Thread count", 4, SLOT_tcount},
344 {"BIT", "32 or 64", 3, SLOT_bits},
345
346 // String fields (-1 is procpid->str, rest are str+offset[1-slot])
347 {"TTY", "Controlling terminal", -8, -2},
348 {"WCHAN", "Wait location in kernel", -6, -3},
349 {"LABEL", "Security label", -30, -4},
350 {"COMM", "EXE filename (/proc/PID/exe)", -27, -5},
351 {"NAME", "Process name (PID's argv[0])", -27, -7},
352 {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5},
353 {"CMDLINE", "Command line (argv[])", -27, -6},
354 {"ARGS", "CMDLINE minus initial path", -27, -6},
355 {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1},
356
357 // user/group (may call getpwuid() or similar)
358 {"UID", "User id", 5, SLOT_uid},
359 {"USER", "User name", -12, XX|SLOT_uid},
360 {"RUID", "Real (before suid) user ID", 4, SLOT_ruid},
361 {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid},
362 {"GID", "Group ID", 8, SLOT_gid},
363 {"GROUP", "Group name", -8, XX|SLOT_gid},
364 {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid},
365 {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid},
366
367 // clock displays (00:00:00)
368 {"TIME", "CPU time consumed", 8, SLOT_utime},
369 {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime},
370 {"TIME+", "CPU time (high precision)", 9, SLOT_utime},
371
372 // Percentage displays (fixed point, one decimal digit. 123 -> 12.3)
373 {"C", "Total %CPU used since start", 1, SLOT_utime2},
374 {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize},
375 {"%MEM", "RSS as % of physical memory", 5, SLOT_rss},
376 {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2},
377
378 // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc)
379 {"VIRT", "Virtual memory size", 4, SLOT_vsz},
380 {"RES", "Short RSS", 4, SLOT_rss},
381 {"SHR", "Shared memory", 4, SLOT_shr},
382 {"READ", "Data read", 6, SLOT_rchar},
383 {"WRITE", "Data written", 6, SLOT_wchar},
384 {"IO", "Data I/O", 6, SLOT_iobytes},
385 {"DREAD", "Data read from disk", 6, SLOT_rbytes},
386 {"DWRITE", "Data written to disk", 6, SLOT_wbytes},
387 {"SWAP", "Swap I/O", 6, SLOT_swap},
388 {"DIO", "Disk I/O", 6, SLOT_diobytes},
389
390 // Misc (special cases)
391 {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime},
392 {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags},
393 {"S", "Process state:\n"
394 "\t R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)\n"
395 "\t X (dead) Z (zombie) P (parked) I (idle)\n"
396 "\t Also between Linux 2.6.33 and 3.13:\n"
397 "\t x (dead) K (wakekill) W (waking)\n",
398 -1, XX},
399 {"STAT", "Process state (S) plus:\n"
400 "\t < high priority N low priority L locked memory\n"
401 "\t s session leader + foreground l multithreaded",
402 -5, XX},
403 {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy},
404 );
405
406 // Show sorted "-o help" text for fields listed in toybuf[len]
help_fields(int len,int multi)407 static void help_fields(int len, int multi)
408 {
409 int i, j, k, left = 0;
410 struct typography *t;
411
412 // Quick and dirty sort of toybuf[] entries (see TODO below)
413 for (j = len; j--; ) {
414 k = -1;
415
416 for (i=0; i<j; i++) {
417 if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) {
418 k = toybuf[i];
419 toybuf[i] = toybuf[i+1];
420 toybuf[i+1] = k;
421 }
422 }
423 if (k == -1) break;
424 }
425
426 // Display loop
427 for (i = j = 0; i<len; i++, j++) {
428 t = (void *)(typos+toybuf[i]);
429 if (strlen(t->help)>30) {
430 if (multi) printf(" %-8s%s\n", t->name, t->help);
431 else j--;
432 } else if (!multi) {
433 left = !(j&1);
434 printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left);
435 }
436 }
437 if (!multi && left) xputc('\n');
438 }
439
440 // Print help text for each -o field, with categories.
help_help(void)441 static void help_help(void)
442 {
443 int i, jump = PS_CMD+1-PS_COMM;
444
445 // TODO: sort the array of -o types so they're already alphabetical and
446 // don't need sorting here. A regex to find everything that currently cares
447 // about symbol order might be: "which *[><]=* *PS"
448
449 // First show the half-dozen variants of command line display.
450
451 printf("Command line field types:\n\n");
452 for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i;
453 help_fields(jump, 0);
454
455 // Show the rest of the -o types, starting with the ones that don't columnize
456
457 printf("\nProcess attribute field types:\n\n");
458 for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump;
459 help_fields(ARRAY_LEN(typos)-jump, 1);
460 help_fields(ARRAY_LEN(typos)-jump, 0);
461
462 xexit();
463 }
464
465 // process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep
shared_match_process(long long * slot)466 static int shared_match_process(long long *slot)
467 {
468 struct ps_ptr_len match[] = {
469 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
470 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
471 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
472 };
473 int i, j;
474 long *ll = 0;
475
476 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
477 for (i = 0; i < ARRAY_LEN(match); i++) {
478 struct ps_ptr_len *mm = match[i].ptr;
479
480 if (mm->len) {
481 ll = mm->ptr;
482 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
483 }
484 }
485
486 return ll ? 0 : -1;
487 }
488
489 // process match filter for ps: Return 0 to discard, nonzero to keep
ps_match_process(long long * slot)490 static int ps_match_process(long long *slot)
491 {
492 int i = shared_match_process(slot);
493
494 if (i>0) return 1;
495 // If we had selections and didn't match them, don't display
496 if (!i) return 0;
497
498 // Filter implicit categories for other display types
499 if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0;
500 if (FLAG(a) && !slot[SLOT_ttynr]) return 0;
501 if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr])
502 return 0;
503
504 return 1;
505 }
506
507 // Generate display string (260 bytes at end of toybuf) from struct ofield
string_field(struct procpid * tb,struct ofields * field)508 static char *string_field(struct procpid *tb, struct ofields *field)
509 {
510 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
511 int which = field->which, sl = typos[which].slot;
512 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0;
513
514 // numbers, mostly from /proc/$PID/stat
515 if (which <= PS_BIT) {
516 char *fmt = "%lld";
517
518 if (which==PS_PRI) ll = 39-ll;
519 if (which==PS_ADDR) fmt = "%llx";
520 else if (which==PS_SZ) ll >>= 12;
521 else if (which==PS_RSS) ll <<= 2;
522 else if (which==PS_VSZ) ll >>= 10;
523 else if (which==PS_PR && ll<-9) fmt="RT";
524 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
525 sprintf(out, fmt, ll);
526
527 // String fields
528 } else if (sl < 0) {
529 out = tb->str;
530 sl *= -1;
531 // First string slot has offset 0, others are offset[-slot-2]
532 if (--sl) out += tb->offset[--sl];
533 if (which==PS_ARGS || which==PS_COMM) {
534 int i;
535
536 s = out;
537 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
538 if (out[i] == '/') s = out+i+1;
539 out = s;
540 }
541 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
542
543 // user/group
544 } else if (which <= PS_RGROUP) {
545 sprintf(out, "%lld", ll);
546 if (sl&XX) {
547 if (which > PS_RUSER) {
548 struct group *gr = bufgetgrgid(ll);
549
550 if (gr) out = gr->gr_name;
551 } else {
552 struct passwd *pw = bufgetpwuid(ll);
553
554 if (pw) out = pw->pw_name;
555 }
556 }
557
558 // Clock displays
559 } else if (which <= PS_TIME_) {
560 int unit = 60, pad = 2, j = TT.ticks;
561 time_t seconds;
562
563 if (which!=PS_TIME_) unit *= 60*24;
564 else pad = 0;
565 // top adjusts slot[SLOT_upticks], we want original meaning.
566 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
567 seconds = ll/j;
568
569 // Output days-hours:mins:secs, skipping non-required fields with zero
570 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
571 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
572 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
573 if (s) {
574 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
575 pad = 2;
576 if ((*s = "-::"[j])) s++;
577 }
578 seconds %= unit;
579 unit /= j ? 60 : 24;
580 }
581 if (which==PS_TIME_ && s-out<8)
582 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
583
584 // Percentage displays
585 } else if (which <= PS__CPU) {
586 ll = slot[sl&(XX-1)]*1000;
587 if (which==PS__VSZ || which==PS__MEM)
588 ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096);
589 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
590 sl = ll;
591 if (which==PS_C) sl += 5;
592 sprintf(out, "%d", sl/10);
593 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
594
595 // Human readable
596 } else if (which <= PS_DIO) {
597 int i = abs(field->len);
598
599 if (i<4) i = 4;
600 s = out;
601 if ((ll = slot[typos[which].slot])<0) {
602 ll = -ll;
603 *s++ = '-';
604 if (i>4) i--;
605 }
606 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
607 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
608 else human_readable_long(s, ll, i-1, 0, 0);
609
610 // Posix doesn't specify what flags should say. Man page says
611 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
612 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
613 else if (which==PS_S || which==PS_STAT) {
614 s = out;
615 *s++ = tb->state;
616 if (which==PS_STAT) {
617 // TODO l = multithreaded
618 if (slot[SLOT_nice]<0) *s++ = '<';
619 else if (slot[SLOT_nice]>0) *s++ = 'N';
620 if (slot[SLOT_sid]==*slot) *s++ = 's';
621 if (slot[SLOT_vmlck]) *s++ = 'L';
622 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
623 }
624 *s = 0;
625 } else if (which==PS_STIME) {
626 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
627
628 // Padding behavior's a bit odd: default field size is just hh:mm.
629 // Increasing stime:size reveals more data at left until full,
630 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
631 // then add :ss on right for :19.
632 strftime(out, 260, "%F %T", localtime(&t));
633 out = out+strlen(out)-3-abs(field->len);
634 if (out<buf) out = buf;
635
636 } else if (which==PS_PCY) sprintf(out, "%.2s", tb->pcy);
637 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
638
639 return out;
640 }
641
642 // Display process data that get_ps() read from /proc, formatting via TT.fields
show_ps(void * p)643 static void show_ps(void *p)
644 {
645 struct procpid *tb = p;
646 struct ofields *field = TT.fields;
647 int pad, len, width = TT.width, abslen, sign, olen, scroll, extra = 0;
648
649 // Skip TT.scroll many fields (but not last one)
650 for (scroll = TT.scroll; scroll && field->next; scroll--) field = field->next;
651
652 // Loop through fields to display
653 for (; field; field = field->next) {
654 char *out = string_field(tb, field);
655
656 // Output the field, appropriately padded
657
658 // Minimum one space between each field
659 if (width<2) break;
660 if (field != TT.fields) {
661 putchar(' ');
662 width--;
663 }
664
665 // Don't truncate number fields, but try to reclaim extra offset from later
666 // fields that can naturally be shorter
667 abslen = abs(field->len);
668 sign = field->len<0 ? -1 : 1;
669 olen = (TT.tty) ? utf8len(out) : strlen(out);
670 if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) {
671 // overflow but remember by how much
672 extra += olen-abslen;
673 abslen = olen;
674 } else if (extra && olen<abslen) {
675 int unused = abslen-olen;
676
677 // If later fields have slack space, take back overflow
678 if (unused>extra) unused = extra;
679 abslen -= unused;
680 extra -= unused;
681 }
682 if (abslen>width) abslen = width;
683 len = pad = abslen;
684 pad *= sign;
685
686 // If last field is left justified, no trailing spaces.
687 if (!field->next && sign<0) {
688 pad = -1;
689 len = width;
690 }
691
692 // If we truncated a left-justified field, show + instead of last char
693 if (olen>len && len>1 && sign<0) {
694 width--;
695 len--;
696 if (field->next) pad++;
697 abslen = 0;
698 }
699
700 if (TT.tty) width -= draw_trim(out, pad, len);
701 else width -= printf("%*.*s", pad, len, out);
702 if (!abslen) putchar('+');
703 if (!width) break;
704 }
705 putchar(TT.time ? '\r' : '\n');
706 }
707
708 // dirtree callback: read data about a process, then display or store it.
709 // Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra
710 // (in -k mode) or calls show_ps directly on toybuf (for low memory systems).
get_ps(struct dirtree * new)711 static int get_ps(struct dirtree *new)
712 {
713 struct {
714 char *name; // Path under /proc/$PID directory
715 long long bits; // Only fetch extra data if an -o field is displaying it
716 } fetch[] = {
717 // sources for procpid->offset[] data
718 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
719 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
720 {"", _PS_NAME}
721 };
722 struct procpid *tb = (void *)toybuf;
723 long long *slot = tb->slot;
724 char *name, *s, *buf = tb->str, *end = 0;
725 FILE *fp;
726 struct sysinfo si;
727 int i, j, fd;
728 off_t len;
729
730 // Recurse one level into /proc children, skip non-numeric entries
731 if (!new->parent)
732 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
733 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
734
735 // Grab PID and figure out if we're a thread or a process
736 memset(slot, 0, sizeof(tb->slot));
737 slot[SLOT_tid] = *slot = atol(new->name);
738 if (TT.threadparent && TT.threadparent->extra) {
739 struct procpid *tb2 = (struct procpid *)TT.threadparent->extra;
740
741 *slot = *tb2->slot;
742 // Parent also shows up as a thread, but we need to reread task/stat fields
743 // to get non-collated info for just parent thread (vs whole process).
744 if (*slot == slot[SLOT_tid]) slot = tb2->slot;
745 }
746 fd = dirtree_parentfd(new);
747
748 // Read /proc/$PID/stat into half of toybuf.
749 len = 2048;
750 sprintf(buf, "%lld/stat", slot[SLOT_tid]);
751 if (!readfileat(fd, buf, buf, &len)) return 0;
752
753 // parse oddball fields: the first field is same as new->name (skip it)
754 // and the second and third (name and state) are the only non-numeric fields.
755 // Name has (parentheses) around it, and can have embedded ')' so match
756 // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that).
757 // TODO: kernel task struct actually limits name to 16 chars?
758 if (!(name = strchr(buf, '('))) return 0;
759 for (s = ++name; *s; s++) if (*s == ')') end = s;
760 if (!end || end-name>255) return 0;
761 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
762
763 // All remaining fields should be numeric, parse them into slot[] array
764 // (skipping first 3 stat fields and first slot[], both were handled above)
765 // yes this means the alignment's off: stat[4] becomes slot[1]
766 for (j = SLOT_ppid; j<SLOT_upticks; j++)
767 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
768
769 // Now we've read the data, move status and name right after slot[] array,
770 // and convert low chars to ? for non-tty display while we're at it.
771 for (i = 0; i<end-name; i++)
772 if ((tb->str[i] = name[i]) < ' ')
773 if (!TT.tty) tb->str[i] = '?';
774 buf = tb->str+i;
775 *buf++ = 0;
776 len = sizeof(toybuf)-(buf-toybuf);
777
778 // Overwrite useless/obsolete stat fields with more interesting data.
779
780 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
781 // or numeric wchan, and the remaining two are always zero), and vmlck into
782 // 18 (which is "obsolete, always 0" from stat)
783 slot[SLOT_uid] = new->st.st_uid;
784 slot[SLOT_gid] = new->st.st_gid;
785
786 // TIME and TIME+ use combined value, ksort needs 'em added.
787 slot[SLOT_utime] += slot[SLOT_stime];
788 slot[SLOT_utime2] = slot[SLOT_utime];
789
790 // Do we need to read "status"?
791 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
792 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
793 {
794 off_t temp = len;
795
796 sprintf(buf, "%lld/status", slot[SLOT_tid]);
797 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
798 s = strafter(buf, "\nUid:");
799 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
800 s = strafter(buf, "\nGid:");
801 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
802 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024;
803 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024;
804 }
805
806 // Do we need to read "io"?
807 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
808 off_t temp = len;
809
810 sprintf(buf, "%lld/io", slot[SLOT_tid]);
811 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
812 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
813 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
814 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
815 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
816 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
817 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
818 }
819
820 // If we were updating thread parent with its own task info, we're done.
821 if (slot != tb->slot) return 0;
822
823 // We now know enough to skip processes we don't care about.
824 if (TT.match_process && !TT.match_process(slot)) return 0;
825
826 // /proc data is generated as it's read, so for maximum accuracy on slow
827 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
828 sysinfo(&si);
829 slot[SLOT_uptime] = si.uptime;
830 slot[SLOT_totalram] = si.totalram;
831 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
832
833 // Do we need to read "statm"?
834 if (TT.bits&(_PS_VIRT|_PS_SHR)) {
835 off_t temp = len;
836
837 sprintf(buf, "%lld/statm", slot[SLOT_tid]);
838 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
839
840 // Skip redundant RSS field, we got it from stat.
841 slot[SLOT_vsz] = slot[SLOT_shr] = 0;
842 sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]);
843 }
844
845 // Do we need to read "exe"?
846 if (TT.bits&_PS_BIT) {
847 off_t temp = 6;
848
849 sprintf(buf, "%lld/exe", slot[SLOT_tid]);
850 if (readfileat(fd, buf, buf, &temp) && !smemcmp(buf, "\177ELF", 4)) {
851 if (buf[4] == 1) slot[SLOT_bits] = 32;
852 else if (buf[4] == 2) slot[SLOT_bits] = 64;
853 }
854 }
855
856 // Do we need Android scheduling policy?
857 if (TT.bits&_PS_PCY) {
858 // Find the cpuset line in "/proc/$pid/cgroup", extract the final field,
859 // and translate it to one of Android's traditional 2-char names.
860 // TODO: if other Linux systems start using cgroups, conditionalize this.
861 sprintf(buf, "/proc/%lld/cgroup", slot[SLOT_tid]);
862 if ((fp = fopen(buf, "re"))) {
863 char *s, *line;
864 while ((line = xgetline(fp))) {
865 if ((s = strstr(line, ":cpuset:/"))) {
866 s += strlen(":cpuset:/");
867 if (!*s || !strcmp(s, "foreground")) strcpy(tb->pcy, "fg");
868 else if (!strcmp(s, "system-background")) strcpy(tb->pcy, " ");
869 else if (!strcmp(s, "background")) strcpy(tb->pcy, "bg");
870 else if (!strcmp(s, "top-app")) strcpy(tb->pcy, "ta");
871 else if (!strcmp(s, "restricted")) strcpy(tb->pcy, "rs");
872 else if (!strcmp(s, "foreground_window")) strcpy(tb->pcy, "wi");
873 else if (!strcmp(s, "camera-daemon")) strcpy(tb->pcy, "cd");
874 else strcpy(tb->pcy, "?");
875 }
876 free(line);
877 }
878 fclose(fp);
879 } else strcpy(tb->pcy, "-");
880 }
881
882 // Done using buf[] (tb->str) as scratch space, now read string data,
883 // saving consective null terminated strings. (Save starting offsets into
884 // str->offset to avoid strlen() loop to find relevant string.)
885
886 // Fetch string data while parentfd still available, appending to buf.
887 // (There's well over 3k of toybuf left. We could dynamically malloc, but
888 // it'd almost never get used, querying length of a proc file is awkward,
889 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
890
891 // The fetch[] array at the start of the function says what file to read
892 // and what -o display field outputs it (to skip the ones we don't need).
893
894 slot[SLOT_argv0len] = 0;
895 for (j = 0; j<ARRAY_LEN(fetch); j++) {
896 tb->offset[j] = buf-(tb->str);
897 if (!(TT.bits&fetch[j].bits)) {
898 *buf++ = 0;
899 continue;
900 }
901
902 // Determine available space: reserve 256 bytes (guaranteed minimum) for
903 // each string we haven't checked yet, tb->str starts after the numeric
904 // arrays in struct procpid, and we reserve 260 bytes scratch space at the
905 // end of toybuf for output conversion in string_field(). Other than that,
906 // each use all available space, and future strings that don't use their
907 // guaranteed minimum add to the pool.
908 len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260;
909 sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
910
911 // For exe (j==3) readlink() instead of reading file's contents
912 // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID).
913 if (j==3 || j==5) {
914 struct procpid *ptb = 0;
915 int k;
916
917 // Thread doesn't have exe or argv[0], so use parent's
918 if (TT.threadparent && TT.threadparent->extra)
919 ptb = (void *)TT.threadparent->extra;
920
921 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
922 else {
923 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
924 else {
925 if (!ptb || slot[SLOT_argv0len]) ptb = tb;
926 i = ptb->slot[SLOT_argv0len];
927 s = ptb->str+ptb->offset[4];
928 while (-1!=(k = stridx(s, '/')) && k<i) {
929 s += k+1;
930 i -= k+1;
931 }
932 }
933 if (i<len) len = i;
934 memcpy(buf, s, len);
935 buf[len] = 0;
936 }
937
938 // Turning stat's SLOT_ttynr into a string is an outright heuristic ordeal.
939 } else if (!j) {
940 int rdev = slot[SLOT_ttynr];
941 struct stat st;
942
943 // Call no tty "?" rather than "0:0".
944 strcpy(buf, "?");
945 if (rdev) {
946 // Can we readlink() our way to a name?
947 for (i = 0; i<3; i++) {
948 sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i);
949 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
950 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
951 break;
952 }
953
954 // Couldn't find it, try all the tty drivers.
955 if (i == 3) {
956 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
957
958 if ((fp = fopen("/proc/tty/drivers", "r"))) {
959 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
960 // TODO: we could parse the minor range too.
961 if (tty_major == maj) {
962 len = strlen(buf);
963 len += sprintf(buf+len, "%d", min);
964 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
965 break;
966 }
967 tty_major = 0;
968 }
969 fclose(fp);
970 }
971
972 // Really couldn't find it, so just show major:minor.
973 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
974 }
975
976 s = buf;
977 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
978 }
979
980 // For the rest, the data we want is in a file we can just read.
981 } else {
982 int temp = 0;
983
984 // When command has no arguments, don't space over the NUL
985 if (readfileat(fd, buf, buf, &len) && len>0) {
986
987 // Trim trailing whitespace and NUL bytes
988 while (len)
989 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
990 else break;
991
992 // Turn NUL to space, other low ascii to ? (in non-tty mode), except
993 // cmdline has a trailing NUL that we don't want to turn to space.
994 for (i=0; i<len-1; i++) {
995 char c = buf[i];
996
997 if (!c) {
998 if (!temp) temp = i;
999 c = ' ';
1000 } else if (!TT.tty && c<' ') c = '?';
1001 buf[i] = c;
1002 }
1003 } else *buf = len = 0;
1004
1005 // Store end of argv[0] so ARGS and CMDLINE can differ.
1006 // We do it for each file string slot but last is cmdline, which sticks.
1007 slot[SLOT_argv0len] = temp ? : len; // Position of _first_ NUL
1008 }
1009
1010 // Each case above calculated/retained len, so we don't need to re-strlen.
1011 buf += len+1;
1012 }
1013
1014 // Record that we saw another process, and display/return now if appropriate
1015 TT.kcount++;
1016 if (TT.show_process && !TT.threadparent) {
1017 TT.show_process(tb);
1018
1019 return 0;
1020 }
1021
1022 // We're retaining data (probably to sort it), save copy in list.
1023 s = xmalloc(buf-toybuf);
1024 new->extra = (long)s;
1025 memcpy(s, toybuf, buf-toybuf);
1026
1027 return DIRTREE_SAVE;
1028 }
1029
1030 // wrapper for get_ps() that also collects threads under each processes
get_threads(struct dirtree * new)1031 static int get_threads(struct dirtree *new)
1032 {
1033 struct dirtree *dt;
1034 struct procpid *tb;
1035 unsigned pid, kcount;
1036
1037 if (!new->parent) return get_ps(new);
1038 pid = atol(new->name);
1039
1040 TT.threadparent = new;
1041 if (!get_ps(new)) {
1042 // it exited out from under us
1043 TT.threadparent = 0;
1044
1045 return 0;
1046 }
1047
1048 // Recurse down into tasks, retaining thread groups.
1049 // Disable show_process at least until we can calculate tcount
1050 kcount = TT.kcount;
1051 sprintf(toybuf, "/proc/%u/task", pid);
1052 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1053 if (new->child == DIRTREE_ABORTVAL) new->child = 0;
1054 TT.threadparent = 0;
1055 kcount = TT.kcount-kcount+1;
1056 tb = (void *)new->extra;
1057 tb->slot[SLOT_tcount] = kcount;
1058
1059 // Fill out tid and thread count for each entry in group (if it didn't exit
1060 // out from under us again; asynchronous reads of unlocked data are fun!)
1061 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
1062 tb = (void *)dt->extra;
1063 tb->slot[SLOT_pid] = pid;
1064 tb->slot[SLOT_tcount] = kcount;
1065 }
1066
1067 // Save or display
1068 if (!TT.show_process) return DIRTREE_SAVE;
1069 TT.show_process((void *)new->extra);
1070 if ((dt = new->child)) {
1071 new->child = 0;
1072 while (dt->child) {
1073 new = dt->child->next;
1074 TT.show_process((void *)dt->child->extra);
1075 free(dt->child);
1076 dt->child = new;
1077 }
1078 free(dt);
1079 }
1080
1081 return 0;
1082 }
1083
1084 // Parse one FIELD argument (with optional =name :width) into struct ofields
parse_ko(void * data,char * type,int length)1085 static char *parse_ko(void *data, char *type, int length)
1086 {
1087 struct ofields *field;
1088 char *width, *title, *end, *s;
1089 int i, j, k;
1090
1091 // Caller's WOULD_EXIT catches -o help and prints help
1092 if (length==4 && !strncasecmp(type, "HELP", length)) xexit();
1093
1094 // Get title, length of title, type, end of type, and display width
1095
1096 // Chip off =name to display
1097 if ((end = strchr(type, '=')) && length>(end-type)) {
1098 title = end+1;
1099 length -= (end-type)+1;
1100 } else {
1101 end = type+length;
1102 title = 0;
1103 }
1104
1105 // Chip off :width to display
1106 if ((width = strchr(type, ':')) && width<end) {
1107 if (!title) length = width-type;
1108 } else width = 0;
1109
1110 // Allocate structure plus extra space to append a copy of title data
1111 // (this way it's same lifetime, freeing struct automatically frees title)
1112 field = xzalloc(sizeof(struct ofields)+(length+1)*!!title);
1113 if (title) {
1114 memcpy(field->title = (char *)(field+1), title, length);
1115 field->title[field->len = length] = 0;
1116 }
1117
1118 if (width) {
1119 field->len = strtol(++width, &title, 10);
1120 if (!isdigit(*width) || title != end) return title;
1121 end = --width;
1122 }
1123
1124 // Find type
1125 field->reverse = 1;
1126 if (*type == '-') field->reverse = -1;
1127 else if (*type != '+') type--;
1128 type++;
1129 for (i = 0; i<ARRAY_LEN(typos); i++) {
1130 field->which = i;
1131 for (j = 0; j<2; j++) {
1132 if (!j) s = typos[i].name;
1133 // posix requires alternate names for some fields
1134 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
1135 PS_VSZ, PS_USER, 0}, i))) continue;
1136 else
1137 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
1138
1139 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
1140 }
1141 if (j!=2) break;
1142 }
1143 if (i==ARRAY_LEN(typos)) return type;
1144 if (!field->title) field->title = typos[field->which].name;
1145 k = i<2 ? TT.pidlen : typos[field->which].width;
1146 if (!field->len) field->len = k;
1147 else if (k<0) field->len *= -1;
1148 dlist_add_nomalloc(data, (void *)field);
1149
1150 return 0;
1151 }
1152
1153 // Write FIELD list into display header string (truncating at blen),
1154 // and return bitfield of which FIELDs are used.
get_headers(struct ofields * field,char * buf,int blen)1155 static long long get_headers(struct ofields *field, char *buf, int blen)
1156 {
1157 long long bits = 0;
1158 int len = 0, scroll;
1159
1160 // Skip TT.scroll many fields (but not last one)
1161 for (scroll = TT.scroll; scroll && field->next; scroll--) field = field->next;
1162
1163 for (; field; field = field->next) {
1164 len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
1165 field->title);
1166 bits |= 1LL<<field->which;
1167 }
1168
1169 return bits;
1170 }
1171
1172 // Parse command line options -p -s -t -u -U -g -G
parse_rest(void * data,char * str,int len)1173 static char *parse_rest(void *data, char *str, int len)
1174 {
1175 struct ps_ptr_len *pl = (struct ps_ptr_len *)data;
1176 long *ll = pl->ptr;
1177 char *end;
1178 int num = 0;
1179
1180 // Allocate next chunk of data
1181 if (!(15&pl->len))
1182 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1183
1184 // Parse numerical input
1185 if (isdigit(*str)) {
1186 ll[pl->len] = xstrtol(str, &end, 10);
1187 if (end==(len+str)) num++;
1188 // For pkill, -s 0 represents pkill's session id.
1189 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1190 }
1191
1192 // PID can't be zero but SID can be 0 before the init task calls setsid().
1193 if (pl==&TT.pp || pl==&TT.ss) {
1194 if (num && ll[pl->len]>-(pl==&TT.ss)) {
1195 pl->len++;
1196
1197 return 0;
1198 }
1199 } else if (pl==&TT.tt) {
1200 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1201 if (!num) {
1202 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1203 if (strstart(&str, "pts/")) {
1204 len -= 4;
1205 num++;
1206 } else if (strstart(&str, "tty")) len -= 3;
1207 }
1208 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1209 struct stat st;
1210
1211 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1212 memcpy(end, str, len);
1213 end[len] = 0;
1214 xstat(toybuf, &st);
1215 ll[pl->len++] = st.st_rdev;
1216
1217 return 0;
1218 }
1219 } else if (len<255) {
1220 char name[256];
1221
1222 if (num) {
1223 pl->len++;
1224
1225 return 0;
1226 }
1227
1228 memcpy(name, str, len);
1229 name[len] = 0;
1230 if (pl==&TT.gg || pl==&TT.GG) {
1231 struct group *gr = getgrnam(name);
1232 if (gr) {
1233 ll[pl->len++] = gr->gr_gid;
1234
1235 return 0;
1236 }
1237 } else if (pl==&TT.uu || pl==&TT.UU) {
1238 struct passwd *pw = getpwnam(name);
1239 if (pw) {
1240 ll[pl->len++] = pw->pw_uid;
1241
1242 return 0;
1243 }
1244 }
1245 }
1246
1247 // Return error
1248 return str;
1249 }
1250
1251 // sort processes by FIELD(s) listed in option -k
ksort(void * aa,void * bb)1252 static int ksort(void *aa, void *bb)
1253 {
1254 struct ofields *field;
1255 struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
1256 int ret = 0, slot;
1257
1258 for (field = TT.kfields; field && !ret; field = field->next) {
1259 slot = typos[field->which].slot;
1260
1261 // Can we do numeric sort?
1262 if (!(slot&XX)) {
1263 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1264 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1265 }
1266
1267 // fallback to string sort
1268 if (!ret) {
1269 memccpy(toybuf, string_field(ta, field), 0, 2048);
1270 toybuf[2048] = 0;
1271 ret = strcmp(toybuf, string_field(tb, field));
1272 }
1273 ret *= field->reverse;
1274 }
1275
1276 return ret;
1277 }
1278
1279 // Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array
1280 // (recursion because tree from get_thread() isn't flat list of siblings)
collate_leaves(struct procpid ** tb,struct dirtree * dt)1281 static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
1282 {
1283 while (dt) {
1284 struct dirtree *next = dt->next;
1285
1286 if (dt->extra) *(tb++) = (void *)dt->extra;
1287 if (dt->child) tb = collate_leaves(tb, dt->child);
1288 free(dt);
1289 dt = next;
1290 }
1291
1292 return tb;
1293 }
1294
1295 // Allocate struct procpid array of length count and populate it with ->extra
1296 // fields from dirtree leaf nodes. (top diffs old & new array to show changes)
collate(int count,struct dirtree * dt)1297 static struct procpid **collate(int count, struct dirtree *dt)
1298 {
1299 struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
1300
1301 collate_leaves(tbsort, dt);
1302
1303 return tbsort;
1304 }
1305
1306 // parse command line arguments (ala -k -o) with a comma separated FIELD list
default_ko(char * s,void * fields,char * err,struct arg_list * arg)1307 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1308 {
1309 struct arg_list def;
1310 int x;
1311
1312 memset(&def, 0, sizeof(struct arg_list));
1313 def.arg = s;
1314 WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
1315 if (x) help_help();
1316 }
1317
common_setup(void)1318 static void common_setup(void)
1319 {
1320 char buf[128];
1321 int i;
1322
1323 TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
1324
1325 if (-1 != (i = tty_fd())) {
1326 struct stat st;
1327
1328 if (!fstat(i, &st)) TT.tty = st.st_rdev;
1329 }
1330
1331 if (readfile("/proc/sys/kernel/pid_max", buf, 128))
1332 while (isdigit(buf[TT.pidlen])) TT.pidlen++;
1333 else TT.pidlen = 6;
1334 }
1335
ps_main(void)1336 void ps_main(void)
1337 {
1338 char **arg;
1339 struct dirtree *dt;
1340 char *not_o;
1341 int i;
1342
1343 common_setup();
1344
1345 // If we can't query terminal size pad to 80 but do -w
1346 TT.width = 80;
1347 if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w;
1348 if (FLAG(w)) TT.width = 99999;
1349
1350 // parse command line options other than -o
1351 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1352 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1353 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1354 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1355 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1356 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1357 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1358 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1359 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1360 dlist_terminate(TT.kfields);
1361
1362 // It's undocumented, but traditionally extra arguments are extra -p args
1363 for (arg = toys.optargs; *arg; arg++)
1364 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg);
1365
1366 // Figure out which fields to display
1367 not_o = "%sTTY,TIME,CMD";
1368 if (FLAG(f))
1369 sprintf(not_o = toybuf+128,
1370 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C");
1371 else if (FLAG(l))
1372 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1373 else if (CFG_TOYBOX_ON_ANDROID)
1374 sprintf(not_o = toybuf+128,
1375 "USER,%%sPPID,VSIZE:10,RSS,WCHAN:10,ADDR:10,S,%s",
1376 FLAG(T) ? "CMD" : "NAME");
1377 sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,");
1378
1379 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1380 if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0);
1381 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1382
1383 if (TT.ps.O) {
1384 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev;
1385 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1386 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next;
1387 }
1388 dlist_terminate(TT.fields);
1389
1390 // -f and -n change the meaning of some fields
1391 if (FLAG(f)||FLAG(n)) {
1392 struct ofields *field;
1393
1394 for (field = TT.fields; field; field = field->next) {
1395 if (FLAG(n) && field->which>=PS_UID
1396 && field->which<=PS_RGROUP && (typos[field->which].slot&XX))
1397 field->which--;
1398 }
1399 }
1400
1401 // Calculate seen fields bit array, and if we aren't deferring printing
1402 // print headers now (for low memory/nommu systems).
1403 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1404 if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf);
1405 if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps;
1406 TT.match_process = ps_match_process;
1407 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1408 (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1409 ? get_threads : get_ps);
1410
1411 if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) {
1412 struct procpid **tbsort = collate(TT.kcount, dt);
1413
1414 if (FLAG(M)) {
1415 for (i = 0; i<TT.kcount; i++) {
1416 struct ofields *field;
1417
1418 for (field = TT.fields; field; field = field->next) {
1419 int len = strlen(string_field(tbsort[i], field));
1420
1421 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1422 }
1423 }
1424
1425 // Now that we've recalculated field widths, re-pad headers again
1426 get_headers(TT.fields, toybuf, sizeof(toybuf));
1427 printf("%.*s\n", TT.width, toybuf);
1428 }
1429
1430 if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort);
1431 for (i = 0; i<TT.kcount; i++) {
1432 show_ps(tbsort[i]);
1433 free(tbsort[i]);
1434 }
1435 if (CFG_TOYBOX_FREE) free(tbsort);
1436 }
1437
1438 if (!TT.kcount) toys.exitval = 1;
1439 if (CFG_TOYBOX_FREE) {
1440 free(TT.gg.ptr);
1441 free(TT.GG.ptr);
1442 free(TT.pp.ptr);
1443 free(TT.PP.ptr);
1444 free(TT.ss.ptr);
1445 free(TT.tt.ptr);
1446 free(TT.uu.ptr);
1447 free(TT.UU.ptr);
1448 llist_traverse(TT.fields, free);
1449 }
1450 }
1451
1452 #define FOR_top
1453 #include "generated/flags.h"
1454
1455 // select which of the -o fields to sort by
setsort(int pos)1456 static void setsort(int pos)
1457 {
1458 struct ofields *field, *field2;
1459 int i = 0;
1460
1461 if (pos<0) pos = 0;
1462
1463 for (field = TT.fields; field; field = field->next) {
1464 if ((TT.sortpos = i++)<pos && field->next) continue;
1465 field2 = TT.kfields;
1466 field2->which = field->which;
1467 field2->len = field->len;
1468 break;
1469 }
1470 }
1471
1472 // If we have both, adjust slot[deltas[]] to be relative to previous
1473 // measurement rather than process start. Stomping old.data is fine
1474 // because we free it after displaying.
merge_deltas(long long * oslot,long long * nslot,int milis)1475 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1476 {
1477 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1478 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1479 int i;
1480
1481 for (i = 0; i<ARRAY_LEN(deltas); i++)
1482 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1483 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1484
1485 return 1;
1486 }
1487
header_line(int line,int rev)1488 static int header_line(int line, int rev)
1489 {
1490 if (!line) return 0;
1491
1492 if (FLAG(b)) puts(toybuf);
1493 else {
1494 printf("%s%-*.*s%s\r\n", rev?"\e[7m":"", rev?TT.width:0, TT.width, toybuf,
1495 rev?"\e[0m":"");
1496 }
1497
1498 return line-1;
1499 }
1500
top_cursor_cleanup(void)1501 static void top_cursor_cleanup(void)
1502 {
1503 xputsn("\e[?25h");
1504 }
1505
1506 // Show a three color bar graph. spans: 0 total size, 1used, 2 nice, 3 sys
bargraph(char * label,unsigned width,unsigned long span[4])1507 static void bargraph(char *label, unsigned width, unsigned long span[4])
1508 {
1509 char percent[16];
1510 long long ll;
1511 unsigned i, color, len;
1512
1513 if (!*span) ++*span;
1514 i = ((span[1]+(unsigned long long)span[2]+span[3])*1000)/ *span;
1515 len = sprintf(percent, "%u.%u", i/10, i%10);
1516
1517 printf("%s[", label);
1518 for (ll = i = color = 0; i<width; i++) {
1519 while (ll<1 && color<4) {
1520 if (color++!=3) {
1521 ll += span[color]*width;
1522 if (ll<*span/2) continue;
1523 }
1524 // green, red, blue, grey
1525 if (color==4) printf("\e[1;2;37m");
1526 else printf("\e[%um", (char[]){32,34,31}[color-1]);
1527 break;
1528 }
1529 if (color<4) ll -= *span;
1530 printf("%c", width-i>len ? (color==4 ? ' ' : '|') : percent[len-(width-i)]);
1531 }
1532 printf("\e[0m]");
1533 }
1534
top_common(int (* filter)(long long * oslot,long long * nslot,int milis))1535 static void top_common(
1536 int (*filter)(long long *oslot, long long *nslot, int milis))
1537 {
1538 long long timeout = 0, now, stats[16];
1539 struct proclist {
1540 struct procpid **tb;
1541 int count;
1542 long long whence;
1543 } plist[2], *plold, *plnew, old, new, mix;
1544 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1545 "iow", "irq", "sirq", "host"};
1546 unsigned tock = 0;
1547 int i, lines, topoff = 0, done = 0;
1548
1549 if (!TT.fields) perror_exit("no -o");
1550
1551 // Avoid flicker and hide the cursor in interactive mode.
1552 if (!FLAG(b)) {
1553 sigatexit(top_cursor_cleanup);
1554 xputsn("\e[?25l");
1555 }
1556
1557 toys.signal = SIGWINCH;
1558 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1559 *scratch = 0;
1560 memset(plist, 0, sizeof(plist));
1561 memset(stats, 0, sizeof(stats));
1562 do {
1563 struct dirtree *dt;
1564 int recalc = 1;
1565
1566 plold = plist+(tock++&1);
1567 plnew = plist+(tock&1);
1568 plnew->whence = millitime();
1569 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1570 (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps);
1571 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
1572 plnew->tb = collate(plnew->count = TT.kcount, dt);
1573 TT.kcount = 0;
1574
1575 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1576 long long *st = stats+8*(tock&1);
1577
1578 // user nice system idle iowait irq softirq host
1579 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1580 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1581 }
1582
1583 // First time, wait a quarter of a second to collect a little delta data.
1584 if (!plold->tb) {
1585 msleep(250);
1586 continue;
1587 }
1588
1589 // Collate old and new into "mix", depends on /proc read in pid sort order
1590 old = *plold;
1591 new = *plnew;
1592 mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
1593 mix.count = 0;
1594
1595 while (old.count || new.count) {
1596 struct procpid *otb = old.count ? *old.tb : 0,
1597 *ntb = new.count ? *new.tb : 0;
1598
1599 // If we just have old for this process, it exited. Discard it.
1600 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1601 old.tb++;
1602 old.count--;
1603
1604 continue;
1605 }
1606
1607 // If we just have new, use it verbatim
1608 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1609 else {
1610 // Keep or discard
1611 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1612 mix.tb[mix.count] = otb;
1613 mix.count++;
1614 }
1615 old.tb++;
1616 old.count--;
1617 }
1618 new.tb++;
1619 new.count--;
1620 }
1621
1622 // Don't re-fetch data if it's not time yet, just re-display existing data.
1623 for (;;) {
1624 char was, is;
1625
1626 if (recalc) {
1627 qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
1628 if (!FLAG(b)) {
1629 printf("\e[H\e[J");
1630 if (toys.signal) {
1631 toys.signal = 0;
1632 terminal_probesize(&TT.width, &TT.height);
1633 }
1634 }
1635 if (TT.top.m) TT.height = TT.top.m+5;
1636 lines = TT.height;
1637 }
1638 if (recalc && !FLAG(q)) {
1639 // Display "top" header.
1640 if (*toys.which->name == 't') {
1641 struct ofields field;
1642 char hr[4][32];
1643 long long ll, up = 0;
1644 long run[6];
1645 int j, k, cpus = sysconf(_SC_NPROCESSORS_CONF);
1646
1647
1648 // Count running, sleeping, stopped, zombie processes.
1649 // The kernel has more states (and different sets in different
1650 // versions), so we need to map them. (R)unning and (Z)ombie are
1651 // easy enough, and since "stopped" is rare (just T and t as of
1652 // Linux 4.20), we assume everything else is "sleeping".
1653 field.which = PS_S;
1654 memset(run, 0, sizeof(run));
1655 for (i = 0; i<mix.count; i++)
1656 run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++;
1657 sprintf(toybuf,
1658 "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, "
1659 "%3ld zombie", FLAG(H) ? "Thread" : "Task", mix.count, run[1],
1660 run[0], run[2]+run[3], run[4]);
1661 lines = header_line(lines, 0);
1662
1663 if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) {
1664 for (i = 0; i<6; i++) {
1665 j = i%3;
1666 pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:",
1667 "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]);
1668 run[i] = pos ? atol(pos) : 0;
1669 if (FLAG(h)) continue;
1670 k = (*run>=10000000);
1671 human_readable_long(hr[j+!!j], run[i]>>(10*k), 9, k+1, HR_NODOT);
1672 if (j==1) human_readable_long(hr[1], (run[i-1]-run[i])>>(10*k),
1673 8, k+1, HR_NODOT);
1674 else if (j==2) {
1675 sprintf(toybuf, " %s:%10s total,%10s used,%10s free,%10s %s",
1676 (i<3) ? " Mem" : "Swap", hr[0], hr[1], hr[2], hr[3],
1677 (i<3) ? "buffers" : "cached");
1678 lines = header_line(lines, 0);
1679 }
1680 }
1681 if (FLAG(h)) {
1682 unsigned long swp[] = {run[3], 0, 0, run[3]-run[4]},
1683 mem[] = {run[0], run[0]-run[1]-run[2]-run[5], run[2], run[5]};
1684
1685 bargraph("Mem", 34, mem);
1686 bargraph(" Swp", 34, swp);
1687 xprintf("\r\n");
1688 }
1689 }
1690 pos = toybuf;
1691 pos += sprintf(pos, "%d%%cpu", cpus*100);
1692 j = 4+(cpus>10);
1693
1694 // If a processor goes idle it's powered down and its idle ticks don't
1695 // advance, so calculate idle time as potential time - used.
1696 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1697 if (!up) up = 1;
1698 now = up*cpus;
1699 ll = stats[3] = stats[11] = 0;
1700 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1701 stats[3] = now - llabs(ll);
1702
1703 for (i = 0; i<8; i++) {
1704 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1705 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1706 }
1707 // Display "iotop" header.
1708 } else {
1709 struct ofields *field;
1710 struct procpid tb;
1711
1712 memset(&tb, 0, sizeof(struct procpid));
1713 pos = stpcpy(toybuf, "Totals:");
1714 for (field = TT.fields; field; field = field->next) {
1715 long long ll, bits = 0;
1716 int slot = typos[field->which].slot&(XX-1);
1717
1718 if (field->which<PS_C || field->which>PS_DIO) continue;
1719 ll = 1LL<<field->which;
1720 if (bits&ll) continue;
1721 bits |= ll;
1722 for (i=0; i<mix.count; i++)
1723 tb.slot[slot] += mix.tb[i]->slot[slot];
1724 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1725 " %s: %*s,", typos[field->which].name,
1726 field->len, string_field(&tb, field));
1727 }
1728 *--pos = 0;
1729 }
1730
1731 lines = header_line(lines, 0);
1732 // print line of header labels for currently displayed fields
1733 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1734 for (i = 0, is = ' '; *pos; pos++) {
1735 was = is;
1736 is = *pos;
1737 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1738 pos[-1] = '[';
1739 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1740 }
1741 if (FLAG(b)) while (isspace(*(pos-1))) --pos;
1742 *pos = 0;
1743 lines = header_line(lines, 1);
1744 }
1745 if (!recalc && !FLAG(b)) printf("\e[%dH\e[J", 1+TT.height-lines);
1746
1747 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1748 // Running processes are shown in bold.
1749 int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R';
1750
1751 if (!FLAG(b) && i) putchar('\n');
1752 if (bold) printf("\e[1m");
1753 show_ps(mix.tb[i+topoff]);
1754 if (bold) printf("\e[m");
1755 }
1756
1757 if (TT.top.n && !--TT.top.n) {
1758 done++;
1759 break;
1760 }
1761
1762 now = millitime();
1763 if (timeout<=now) timeout = new.whence+TT.top.d;
1764 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1765
1766 fflush(stdout);
1767
1768 // In batch mode, we ignore the keyboard.
1769 if (FLAG(b)) {
1770 msleep(timeout-now);
1771 // Make an obvious gap between datasets.
1772 xputs("\n\n");
1773 break;
1774 }
1775
1776 recalc = 1;
1777 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1778 if (i==-1 || i==3 || toupper(i)=='Q') {
1779 done++;
1780 break;
1781 }
1782 if (i==-2) break;
1783 if (i==-3) continue;
1784
1785 // Flush unknown escape sequences.
1786 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1787 else if (i=='\r' || i==' ') {
1788 timeout = 0;
1789 break;
1790 } else if (toupper(i)=='R')
1791 ((struct ofields *)TT.kfields)->reverse *= -1;
1792 else {
1793 i -= 256;
1794 if (i == (KEY_SHIFT|KEY_LEFT)) setsort(TT.sortpos-1);
1795 else if (i == (KEY_SHIFT|KEY_RIGHT)) setsort(TT.sortpos+1);
1796 else if (i == KEY_RIGHT) TT.scroll++;
1797 else if (i == KEY_LEFT && TT.scroll) TT.scroll--;
1798 else if (recalc-- && i == KEY_UP) topoff--;
1799 else if (i == KEY_DOWN) topoff++;
1800 else if (i == KEY_PGDN) topoff += lines;
1801 else if (i == KEY_PGUP) topoff -= lines;
1802 else continue;
1803 if (topoff<0) topoff = 0;
1804 if (topoff>mix.count) topoff = mix.count;
1805 }
1806 }
1807
1808 free(mix.tb);
1809 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1810 free(plold->tb);
1811 } while (!done);
1812
1813 if (!FLAG(b)) tty_reset();
1814 }
1815
top_setup(char * defo,char * defk)1816 static void top_setup(char *defo, char *defk)
1817 {
1818 common_setup();
1819
1820 // Are we doing "batch" output or interactive?
1821 if (FLAG(b)) TT.width = TT.height = 99999;
1822 else {
1823 // Grab starting time, make terminal raw, switch off cursor,
1824 // set signal handler to put terminal/cursor back to normal at exit.
1825 TT.time = millitime();
1826 start_redraw(&TT.width, &TT.height);
1827 }
1828
1829 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1830 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1831 TT.match_process = shared_match_process;
1832
1833 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1834 dlist_terminate(TT.fields);
1835
1836 // First (dummy) sort field is overwritten by setsort()
1837 default_ko("-S", &TT.kfields, 0, 0);
1838 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1839 dlist_terminate(TT.kfields);
1840 setsort(TT.top.s-1);
1841 }
1842
top_main(void)1843 void top_main(void)
1844 {
1845 sprintf(toybuf, "%cID,USER,%s%%CPU,%%MEM,TIME+,%s", FLAG(H) ? 'T' : 'P',
1846 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1847 FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1848 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1849 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1850 if (TT.top.O) {
1851 struct ofields *field = TT.fields;
1852
1853 field = field->next->next;
1854 comma_args(TT.top.O, &field, "bad -O", parse_ko);
1855 }
1856
1857 top_common(merge_deltas);
1858 }
1859
1860 #define FOR_iotop
1861 #include "generated/flags.h"
1862
1863 // Compare old and new proces lists to measure changes
iotop_filter(long long * oslot,long long * nslot,int milis)1864 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1865 {
1866 // Current I/O, or accumulated since process start?
1867 if (!FLAG(a)) merge_deltas(oslot, nslot, milis);
1868 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1869
1870 return !FLAG(O) || oslot[SLOT_iobytes+!FLAG(A)];
1871 }
1872
iotop_main(void)1873 void iotop_main(void)
1874 {
1875 char *s1 = 0, *s2 = 0, *d = "D"+FLAG(A);
1876
1877 if (FLAG(K)) TT.forcek++;
1878
1879 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1880 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1881 free(s1);
1882 free(s2);
1883 top_common(iotop_filter);
1884 }
1885
1886 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1887 // context, so force pgrep's flags on even when building pkill standalone.
1888 // (All the pgrep/pkill functions drop out when building ps standalone.)
1889 #define FORCE_FLAGS
1890 #define FOR_pgrep
1891 #include "generated/flags.h"
1892
1893 struct regex_list {
1894 struct regex_list *next;
1895 regex_t reg;
1896 };
1897
do_pgk(struct procpid * tb)1898 static void do_pgk(struct procpid *tb)
1899 {
1900 if (TT.pgrep.signal) {
1901 if (kill(*tb->slot, TT.pgrep.signal)) {
1902 char *s = num_to_sig(TT.pgrep.signal);
1903
1904 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1905 perror_msg("%s->%lld", s, *tb->slot);
1906 }
1907 }
1908 if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) {
1909 printf("%lld", *tb->slot);
1910 if (FLAG(l)) printf(" %s", tb->str+tb->offset[4]*FLAG(f));
1911 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1912 }
1913 }
1914
match_pgrep(void * p)1915 static void match_pgrep(void *p)
1916 {
1917 struct procpid *tb = p;
1918 regmatch_t match;
1919 struct regex_list *reg;
1920 char *name = tb->str+tb->offset[4]*FLAG(f);
1921
1922 // Never match ourselves.
1923 if (TT.pgrep.self == *tb->slot) return;
1924
1925 if (TT.pgrep.regexes) {
1926 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1927 if (regexec(®->reg, name, 1, &match, 0)) continue;
1928 if (FLAG(x))
1929 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1930 break;
1931 }
1932 if (!FLAG(v) == !reg) return;
1933 }
1934
1935 // pgrep should return success if there's a match.
1936 toys.exitval = 0;
1937
1938 // Repurpose a field for -c count.
1939 TT.sortpos++;
1940 if (FLAG(n)||FLAG(o)) {
1941 long long ll = tb->slot[SLOT_starttime];
1942
1943 if (FLAG(o)) ll *= -1;
1944 if (TT.time && TT.time>ll) return;
1945 TT.time = ll;
1946 free(TT.pgrep.snapshot);
1947 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1948 } else do_pgk(tb);
1949 }
1950
pgrep_match_process(long long * slot)1951 static int pgrep_match_process(long long *slot)
1952 {
1953 return !FLAG(v) == !!shared_match_process(slot);
1954 }
1955
pgrep_main(void)1956 void pgrep_main(void)
1957 {
1958 char **arg;
1959 struct regex_list *reg;
1960
1961 TT.pgrep.self = getpid();
1962
1963 // No signal names start with "L", so no need for "L: " in optstr.
1964 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1965 error_exit("bad -L '%s'", TT.pgrep.L);
1966
1967 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1968 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1969 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1970 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1971 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1972 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1973 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1974
1975 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1976 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1977 if (!toys.optc) help_exit("No PATTERN");
1978
1979 if (FLAG(f)) TT.bits |= _PS_CMDLINE;
1980 for (arg = toys.optargs; *arg; arg++) {
1981 reg = xmalloc(sizeof(struct regex_list));
1982 xregcomp(®->reg, *arg, REG_EXTENDED);
1983 reg->next = TT.pgrep.regexes;
1984 TT.pgrep.regexes = reg;
1985 }
1986 TT.match_process = pgrep_match_process;
1987 TT.show_process = match_pgrep;
1988
1989 // pgrep should return failure if there are no matches.
1990 toys.exitval = 1;
1991
1992 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1993 if (FLAG(c)) printf("%d\n", TT.sortpos);
1994 if (TT.pgrep.snapshot) {
1995 do_pgk(TT.pgrep.snapshot);
1996 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1997 }
1998 if (TT.pgrep.d) xputc('\n');
1999 }
2000
2001 #define FOR_pkill
2002 #include "generated/flags.h"
2003
pkill_main(void)2004 void pkill_main(void)
2005 {
2006 char **args = toys.optargs;
2007
2008 if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
2009 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
2010 if (FLAG(V)) TT.tty = 1;
2011 pgrep_main();
2012 }
2013