OSDN Git Service

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