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