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