OSDN Git Service

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