OSDN Git Service

ps: Show [stat2] for any otherwise blank process name field, expand TNAME to 27,
[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,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     Command line (argv[] -path)    CMD    COMM, or ARGS with -f
95       CMDLINE  Command line (argv[])          COMM   Original command name (stat[2])
96       COMMAND  Command name (/proc/$PID/exe)  NAME   Command name (COMMAND -path)
97       TNAME    Thread 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", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
343   {"VSZ", 6, 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", -15, -1},
350   {"NAME", -15, -5}, {"TNAME", -27, -7}, {"COMMAND", -27, -5},
351   {"CMDLINE", -27, -6}, {"ARGS", -27, -6}, {"CMD", -27, -1},
352
353   // user/group
354   {"UID", 5, SLOT_uid}, {"USER", -8, 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_NAME) {
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(struct carveup *tb)
549 {
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     if (field->which<=PS_BIT || extra) olen = strlen(out);
570     if (field->which<=PS_BIT && 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) pad = 0;
586
587     if (TT.tty) width -= draw_trim(out, pad, len);
588     else width -= printf("%*.*s", pad, len, out);
589     if (!width) break;
590   }
591   xputc(TT.time ? '\r' : '\n');
592 }
593
594 // dirtree callback: read data about process to display, store, or discard it.
595 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
596 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
597 static int get_ps(struct dirtree *new)
598 {
599   struct {
600     char *name;     // Path under /proc/$PID directory
601     long long bits; // Only fetch extra data if an -o field is displaying it
602   } fetch[] = {
603     // sources for carveup->offset[] data
604     {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
605     {"exe", _PS_COMMAND|_PS_NAME}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_TNAME},
606     {"", _PS_TNAME}
607   };
608   struct carveup *tb = (void *)toybuf;
609   long long *slot = tb->slot;
610   char *name, *s, *buf = tb->str, *end = 0;
611   int i, j, fd;
612   off_t len;
613
614   // Recurse one level into /proc children, skip non-numeric entries
615   if (!new->parent)
616     return DIRTREE_RECURSE|DIRTREE_SHUTUP
617       |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
618
619   memset(slot, 0, sizeof(tb->slot));
620   if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
621   if (TT.threadparent && TT.threadparent->extra)
622     if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
623   fd = dirtree_parentfd(new);
624
625   len = 2048;
626   sprintf(buf, "%lld/stat", *slot);
627   if (!readfileat(fd, buf, buf, &len)) return 0;
628
629   // parse oddball fields (name and state). Name can have embedded ')' so match
630   // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
631   // All remaining fields should be numeric.
632   if (!(name = strchr(buf, '('))) return 0;
633   for (s = ++name; *s; s++) if (*s == ')') end = s;
634   if (!end || end-name>255) return 0;
635
636   // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
637   if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
638   for (j = 1; j<SLOT_count; j++)
639     if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
640
641   // Now we've read the data, move status and name right after slot[] array,
642   // and convert low chars to ? for non-tty display while we're at it.
643   for (i = 0; i<end-name; i++)
644     if ((tb->str[i] = name[i]) < ' ')
645       if (!TT.tty) tb->str[i] = '?';
646   buf = tb->str+i;
647   *buf++ = 0;
648   len = sizeof(toybuf)-(buf-toybuf);
649
650   // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
651   // or numeric wchan, and the remaining two are always zero), and vmlck into
652   // 18 (which is "obsolete, always 0" from stat)
653   slot[SLOT_uid] = new->st.st_uid;
654   slot[SLOT_gid] = new->st.st_gid;
655
656   // TIME and TIME+ use combined value, ksort needs 'em added.
657   slot[SLOT_utime] += slot[SLOT_stime];
658   slot[SLOT_utime2] = slot[SLOT_utime];
659
660   // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
661   // and save ruid, rgid, and vmlck.
662   if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
663                |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
664   {
665     off_t temp = len;
666
667     sprintf(buf, "%lld/status", *slot);
668     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
669     s = strafter(buf, "\nUid:");
670     slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
671     s = strafter(buf, "\nGid:");
672     slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
673     if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
674     if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
675   }
676
677   // Do we need to read "io"?
678   if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
679     off_t temp = len;
680
681     sprintf(buf, "%lld/io", *slot);
682     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
683     if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
684     if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
685     if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
686     if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
687     slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
688     slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
689   }
690
691   // We now know enough to skip processes we don't care about.
692   if (TT.match_process && !TT.match_process(slot)) return 0;
693
694   // /proc data is generated as it's read, so for maximum accuracy on slow
695   // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
696   sysinfo(&TT.si);
697   slot[SLOT_uptime] = TT.si.uptime;
698   slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
699
700   // Do we need to read "statm"?
701   if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
702     off_t temp = len;
703
704     sprintf(buf, "%lld/statm", *slot);
705     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
706     
707     for (s = buf, i=0; i<3; i++)
708       if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
709       else s += j;
710   }
711
712   // Do we need to read "exe"?
713   if (TT.bits&_PS_BIT) {
714     off_t temp = 6;
715
716     sprintf(buf, "%lld/exe", *slot);
717     if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
718       if (buf[4] == 1) slot[SLOT_bits] = 32;
719       else if (buf[4] == 2) slot[SLOT_bits] = 64;
720     }
721   }
722
723   // Do we need Android scheduling policy?
724   if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
725
726   // Fetch string data while parentfd still available, appending to buf.
727   // (There's well over 3k of toybuf left. We could dynamically malloc, but
728   // it'd almost never get used, querying length of a proc file is awkward,
729   // fixed buffer is nommu friendly... Wait for somebody to complain. :)
730   slot[SLOT_argv0len] = 0;
731   for (j = 0; j<ARRAY_LEN(fetch); j++) {
732     tb->offset[j] = buf-(tb->str);
733     if (!(TT.bits&fetch[j].bits)) {
734       *buf++ = 0;
735       continue;
736     }
737
738     // Determine remaining space, reserving minimum of 256 bytes/field and
739     // 260 bytes scratch space at the end (for output conversion later).
740     len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
741     sprintf(buf, "%lld/%s", *slot, fetch[j].name);
742
743     // For exe we readlink instead of read contents
744     if (j==3 || j==5) {
745       struct carveup *ptb = 0;
746       int k;
747
748       // Thread doesn't have exe or argv[0], so use parent's
749       if (TT.threadparent && TT.threadparent->extra)
750         ptb = (void *)TT.threadparent->extra;
751
752       if (j==3 && !ptb) {
753         if ((len = readlinkat(fd, buf, buf, len))<1) len = 0;
754       } else {
755         if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
756         else {
757           if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
758           i = ptb->slot[SLOT_argv0len];
759           s = ptb->str+ptb->offset[4];
760           while (-1!=(k = stridx(s, '/')) && k<i) {
761             s += k+1;
762             i -= k+1;
763           }
764         }
765         if (i<len) len = i;
766         memcpy(buf, s, len);
767       }
768       buf[len] = 0;
769
770     // If it's not the TTY field, data we want is in a file.
771     // Last length saved in slot[] is command line (which has embedded NULs)
772     } else if (!j) {
773       int rdev = slot[SLOT_ttynr];
774       struct stat st;
775
776       // Call no tty "?" rather than "0:0".
777       strcpy(buf, "?");
778       if (rdev) {
779         // Can we readlink() our way to a name?
780         for (i = 0; i<3; i++) {
781           sprintf(buf, "%lld/fd/%i", *slot, i);
782           if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
783             && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
784           {
785             buf[len] = 0;
786             break;
787           }
788         }
789
790         // Couldn't find it, try all the tty drivers.
791         if (i == 3) {
792           FILE *fp = fopen("/proc/tty/drivers", "r");
793           int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
794
795           if (fp) {
796             while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
797               // TODO: we could parse the minor range too.
798               if (tty_major == maj) {
799                 len = strlen(buf);
800                 len += sprintf(buf+len, "%d", min);
801                 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
802                   break;
803               }
804               tty_major = 0;
805             }
806             fclose(fp);
807           }
808
809           // Really couldn't find it, so just show major:minor.
810           if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
811         }
812
813         s = buf;
814         if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
815       }
816
817     // Data we want is in a file.
818     // Last length saved in slot[] is command line (which has embedded NULs)
819     } else {
820
821       // When command has no arguments, don't space over the NUL
822       if (readfileat(fd, buf, buf, &len) && len>0) {
823         int temp = 0;
824
825         // Trim trailing whitespace and NUL bytes
826         while (len)
827           if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
828           else break;
829
830         // Turn NUL to space, other low ascii to ? (in non-tty mode)
831         // cmdline has a trailing NUL that we don't want to turn to space.
832         for (i=0; i<len-1; i++) {
833           char c = buf[i];
834
835           if (!c) {
836             if (!temp) temp = i;
837             c = ' ';
838           } else if (!TT.tty && c<' ') c = '?';
839           buf[i] = c;
840         }
841         // Store end of argv[0] so ARGS and CMDLINE can differ.
842         // We do it for each file string slot but last is cmdline, which sticks.
843         slot[SLOT_argv0len] = temp ? temp : len;  // Position of _first_ NUL
844       } else *buf = len = 0;
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   struct dirtree *dt;
1157   char *not_o;
1158   int i;
1159
1160   if (toys.optflags&FLAG_w) TT.width = 99999;
1161   shared_main();
1162
1163   // parse command line options other than -o
1164   comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1165   comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1166   comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1167   comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1168   comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1169   comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1170   comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1171   comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1172   comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1173   dlist_terminate(TT.kfields);
1174
1175   // Figure out which fields to display
1176   not_o = "%sTTY,TIME,CMD";
1177   if (toys.optflags&FLAG_f)
1178     sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,CMD",
1179       (toys.optflags&FLAG_T) ? "TCNT" : "C");
1180   else if (toys.optflags&FLAG_l)
1181     not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1182   else if (CFG_TOYBOX_ON_ANDROID)
1183     not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1184   sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1185
1186   // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1187   if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1188   default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1189
1190   if (TT.ps.O) {
1191     if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1192     comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1193     if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1194   }
1195   dlist_terminate(TT.fields);
1196
1197   // -f and -n change the meaning of some fields
1198   if (toys.optflags&(FLAG_f|FLAG_n)) {
1199     struct strawberry *ever;
1200
1201     for (ever = TT.fields; ever; ever = ever->next) {
1202       if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1203       if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1204         && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1205           ever->which--;
1206     }
1207   }
1208
1209   // Calculate seen fields bit array, and if we aren't deferring printing
1210   // print headers now (for low memory/nommu systems).
1211   TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1212   if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1213   if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1214   TT.match_process = ps_match_process;
1215   dt = dirtree_read("/proc",
1216     ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT|_PS_TNAME)))
1217       ? get_threads : get_ps);
1218
1219   if (toys.optflags&(FLAG_k|FLAG_M)) {
1220     struct carveup **tbsort = collate(TT.kcount, dt);
1221
1222     if (toys.optflags&FLAG_M) {
1223       for (i = 0; i<TT.kcount; i++) {
1224         struct strawberry *field;
1225
1226         for (field = TT.fields; field; field = field->next) {
1227           int len = strlen(string_field(tbsort[i], field));
1228
1229           if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1230         }
1231       }
1232
1233       // Now that we've recalculated field widths, re-pad headers again
1234       get_headers(TT.fields, toybuf, sizeof(toybuf));
1235       printf("%.*s\n", TT.width, toybuf);
1236     }
1237
1238     if (toys.optflags&FLAG_k)
1239       qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1240     for (i = 0; i<TT.kcount; i++) {
1241       show_ps(tbsort[i]);
1242       free(tbsort[i]);
1243     }
1244     if (CFG_TOYBOX_FREE) free(tbsort);
1245   }
1246
1247   if (CFG_TOYBOX_FREE) {
1248     free(TT.gg.ptr);
1249     free(TT.GG.ptr);
1250     free(TT.pp.ptr);
1251     free(TT.PP.ptr);
1252     free(TT.ss.ptr);
1253     free(TT.tt.ptr);
1254     free(TT.uu.ptr);
1255     free(TT.UU.ptr);
1256     llist_traverse(TT.fields, free);
1257   }
1258 }
1259
1260 #define CLEANUP_ps
1261 #define FOR_top
1262 #include "generated/flags.h"
1263
1264 // select which of the -o fields to sort by
1265 static void setsort(int pos)
1266 {
1267   struct strawberry *field, *going2;
1268   int i = 0;
1269
1270   if (pos<0) pos = 0;
1271
1272   for (field = TT.fields; field; field = field->next) {
1273     if ((TT.sortpos = i++)<pos && field->next) continue;
1274     going2 = TT.kfields;
1275     going2->which = field->which;
1276     going2->len = field->len;
1277     break;
1278   }
1279 }
1280
1281 // If we have both, adjust slot[deltas[]] to be relative to previous
1282 // measurement rather than process start. Stomping old.data is fine
1283 // because we free it after displaying.
1284 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1285 {
1286   char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1287                    SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1288   int i;
1289
1290   for (i = 0; i<ARRAY_LEN(deltas); i++)
1291     oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1292   oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1293
1294   return 1;
1295 }
1296
1297 static int header_line(int line, int rev)
1298 {
1299   if (!line) return 0;
1300
1301   if (toys.optflags&FLAG_b) rev = 0;
1302
1303   printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1304     (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1305     rev ? "\033[0m" : "");
1306
1307   return line-1;
1308 }
1309
1310 static long long millitime(void)
1311 {
1312   struct timespec ts;
1313
1314   clock_gettime(CLOCK_MONOTONIC, &ts);
1315   return ts.tv_sec*1000+ts.tv_nsec/1000000;
1316 }
1317
1318 static void top_common(
1319   int (*filter)(long long *oslot, long long *nslot, int milis))
1320 {
1321   long long timeout = 0, now, stats[16];
1322   struct proclist {
1323     struct carveup **tb;
1324     int count;
1325     long long whence;
1326   } plist[2], *plold, *plnew, old, new, mix;
1327   char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1328     "iow", "irq", "sirq", "host"};
1329  
1330   unsigned tock = 0;
1331   int i, lines, topoff = 0, done = 0;
1332
1333   toys.signal = SIGWINCH;
1334   TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1335   *scratch = 0;
1336   memset(plist, 0, sizeof(plist));
1337   memset(stats, 0, sizeof(stats));
1338   do {
1339     struct dirtree *dt;
1340     int recalc = 1;
1341
1342     plold = plist+(tock++&1);
1343     plnew = plist+(tock&1);
1344     plnew->whence = millitime();
1345     dt = dirtree_read("/proc",
1346       ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1347         ? get_threads : get_ps);
1348     plnew->tb = collate(plnew->count = TT.kcount, dt);
1349     TT.kcount = 0;
1350
1351     if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1352       long long *st = stats+8*(tock&1);
1353
1354       // user nice system idle iowait irq softirq host
1355       sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1356         st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1357     }
1358
1359     // First time, wait a quarter of a second to collect a little delta data.
1360     if (!plold->tb) {
1361       msleep(250);
1362       continue;
1363     }
1364
1365     // Collate old and new into "mix", depends on /proc read in pid sort order
1366     old = *plold;
1367     new = *plnew;
1368     mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1369     mix.count = 0;
1370
1371     while (old.count || new.count) {
1372       struct carveup *otb = *old.tb, *ntb = *new.tb;
1373
1374       // If we just have old for this process, it exited. Discard it.
1375       if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1376         old.tb++;
1377         old.count--;
1378
1379         continue;
1380       }
1381
1382       // If we just have new, use it verbatim
1383       if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1384       else {
1385         // Keep or discard
1386         if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1387           mix.tb[mix.count] = otb;
1388           mix.count++;
1389         }
1390         old.tb++;
1391         old.count--;
1392       }
1393       new.tb++;
1394       new.count--;
1395     }
1396
1397     // Don't re-fetch data if it's not time yet, just re-display existing data.
1398     for (;;) {
1399       char was, is;
1400
1401       if (recalc) {
1402         qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1403         if (!(toys.optflags&FLAG_b)) {
1404           printf("\033[H\033[J");
1405           if (toys.signal) {
1406             toys.signal = 0;
1407             terminal_probesize(&TT.width, &TT.height);
1408           }
1409         }
1410         lines = TT.height;
1411       }
1412       if (recalc && !(toys.optflags&FLAG_q)) {
1413         // Display "top" header.
1414         if (*toys.which->name == 't') {
1415           struct strawberry alluc;
1416           long long ll, up = 0;
1417           long run[6];
1418           int j;
1419
1420           // Count running, sleeping, stopped, zombie processes.
1421           alluc.which = PS_S;
1422           memset(run, 0, sizeof(run));
1423           for (i = 0; i<mix.count; i++)
1424             run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1425           sprintf(toybuf,
1426             "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1427             "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1428           lines = header_line(lines, 0);
1429
1430           if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1431             for (i=0; i<6; i++) {
1432               pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1433                     "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1434               run[i] = pos ? atol(pos) : 0;
1435             }
1436             sprintf(toybuf,
1437              "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1438               run[0], run[0]-run[1], run[1], run[2]);
1439             lines = header_line(lines, 0);
1440             sprintf(toybuf,
1441               "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1442               run[4], run[4]-run[5], run[5], run[3]);
1443             lines = header_line(lines, 0);
1444           }
1445
1446           pos = toybuf;
1447           i = sysconf(_SC_NPROCESSORS_CONF);
1448           pos += sprintf(pos, "%d%%cpu", i*100);
1449           j = 4+(i>10);
1450
1451           // If a processor goes idle it's powered down and its idle ticks don't
1452           // advance, so calculate idle time as potential time - used.
1453           if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1454           if (!up) up = 1;
1455           now = up*i;
1456           ll = stats[3] = stats[11] = 0;
1457           for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1458           stats[3] = now - llabs(ll);
1459
1460           for (i = 0; i<8; i++) {
1461             ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1462             pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1463           }
1464           lines = header_line(lines, 0);
1465         } else {
1466           struct strawberry *fields;
1467           struct carveup tb;
1468
1469           memset(&tb, 0, sizeof(struct carveup));
1470           pos = stpcpy(toybuf, "Totals:");
1471           for (fields = TT.fields; fields; fields = fields->next) {
1472             long long ll, bits = 0;
1473             int slot = typos[fields->which].slot&63;
1474
1475             if (fields->which<PS_C || fields->which>PS_DIO) continue;
1476             ll = 1LL<<fields->which;
1477             if (bits&ll) continue;
1478             bits |= ll;
1479             for (i=0; i<mix.count; i++)
1480               tb.slot[slot] += mix.tb[i]->slot[slot];
1481             pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1482               " %s: %*s,", typos[fields->which].name,
1483               fields->len, string_field(&tb, fields));
1484           }
1485           *--pos = 0;
1486           lines = header_line(lines, 0);
1487         }
1488
1489         get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1490         for (i = 0, is = ' '; *pos; pos++) {
1491           was = is;
1492           is = *pos;
1493           if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1494             pos[-1] = '[';
1495           if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1496         }
1497         *pos = 0;
1498         lines = header_line(lines, 1);
1499       }
1500       if (!recalc && !(toys.optflags&FLAG_b))
1501         printf("\033[%dH\033[J", 1+TT.height-lines);
1502       recalc = 1;
1503
1504       for (i = 0; i<lines && i+topoff<mix.count; i++) {
1505         if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1506         show_ps(mix.tb[i+topoff]);
1507       }
1508
1509       if (TT.top.n && !--TT.top.n) {
1510         done++;
1511         break;
1512       }
1513
1514       now = millitime();
1515       if (timeout<=now) timeout = new.whence+TT.top.d;
1516       if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1517
1518       // In batch mode, we ignore the keyboard.
1519       if (toys.optflags&FLAG_b) {
1520         msleep(timeout-now);
1521         // Make an obvious gap between datasets.
1522         xputs("\n\n");
1523         continue;
1524       }
1525
1526       i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1527       if (i==-1 || i==3 || toupper(i)=='Q') {
1528         done++;
1529         break;
1530       }
1531       if (i==-2) break;
1532
1533       // Flush unknown escape sequences.
1534       if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1535       else if (i==' ') {
1536         timeout = 0;
1537         break;
1538       } else if (toupper(i)=='R')
1539         ((struct strawberry *)TT.kfields)->reverse *= -1;
1540       else {
1541         i -= 256;
1542         if (i == KEY_LEFT) setsort(TT.sortpos-1);
1543         else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1544         // KEY_UP is 0, so at end of strchr
1545         else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1546           recalc = 0;
1547
1548           if (i == KEY_UP) topoff--;
1549           else if (i == KEY_DOWN) topoff++;
1550           else if (i == KEY_PGDN) topoff += lines;
1551           else if (i == KEY_PGUP) topoff -= lines;
1552           if (topoff<0) topoff = 0; 
1553           if (topoff>mix.count) topoff = mix.count;
1554         }
1555       }
1556       continue;
1557     }
1558
1559     free(mix.tb);
1560     for (i=0; i<plold->count; i++) free(plold->tb[i]);
1561     free(plold->tb);
1562   } while (!done);
1563
1564   if (!(toys.optflags&FLAG_b)) tty_reset();
1565 }
1566
1567 static void top_setup(char *defo, char *defk)
1568 {
1569   TT.top.d *= 1000;
1570   if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1571   else {
1572     TT.time = millitime();
1573     set_terminal(0, 1, 0);
1574     sigatexit(tty_sigreset);
1575     xsignal(SIGWINCH, generic_signal);
1576     printf("\033[?25l\033[0m");
1577   }
1578   shared_main();
1579
1580   comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1581   comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1582   TT.match_process = shared_match_process;
1583
1584   default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1585   dlist_terminate(TT.fields);
1586
1587   // First (dummy) sort field is overwritten by setsort()
1588   default_ko("-S", &TT.kfields, 0, 0);
1589   default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1590   dlist_terminate(TT.kfields);
1591   setsort(TT.top.s-1);
1592 }
1593
1594 void top_main(void)
1595 {
1596   // usage: [-h HEADER] -o OUTPUT -k SORT
1597
1598   sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1599     TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1600   if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1601   top_setup(toybuf, "-%CPU,-ETIME,-PID");
1602   if (TT.top.O) {
1603     struct strawberry *fields = TT.fields;
1604
1605     fields = fields->next->next;
1606     comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1607   }
1608
1609   top_common(merge_deltas);
1610 }
1611
1612 #define CLEANUP_top
1613 #define FOR_iotop
1614 #include "generated/flags.h"
1615
1616 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1617 {
1618   if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1619   else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1620
1621   return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1622 }
1623
1624 void iotop_main(void)
1625 {
1626   char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1627
1628   if (toys.optflags&FLAG_K) TT.forcek++;
1629
1630   top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1631     s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1632   free(s1);
1633   free(s2);
1634   top_common(iotop_filter);
1635 }
1636
1637 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1638 // context, so force pgrep's flags on even when building pkill standalone.
1639 // (All the pgrep/pkill functions drop out when building ps standalone.)
1640 #define FORCE_FLAGS
1641 #define CLEANUP_iotop
1642 #define FOR_pgrep
1643 #include "generated/flags.h"
1644
1645 struct regex_list {
1646   struct regex_list *next;
1647   regex_t reg;
1648 };
1649
1650 static void do_pgk(struct carveup *tb)
1651 {
1652   if (TT.pgrep.signal) {
1653     if (kill(*tb->slot, TT.pgrep.signal)) {
1654       char *s = num_to_sig(TT.pgrep.signal);
1655
1656       if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1657       perror_msg("%s->%lld", s, *tb->slot);
1658     }
1659   }
1660   if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1661     printf("%lld", *tb->slot);
1662     if (toys.optflags&FLAG_l)
1663       printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1664     
1665     printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1666   }
1667 }
1668
1669 static void match_pgrep(struct carveup *tb)
1670 {
1671   regmatch_t match;
1672   struct regex_list *reg;
1673   char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1674
1675   // Never match ourselves.
1676   if (TT.pgrep.self == *tb->slot) return;
1677
1678   if (TT.pgrep.regexes) {
1679     for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1680       if (regexec(&reg->reg, name, 1, &match, 0)) continue;
1681       if (toys.optflags&FLAG_x)
1682         if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1683       break;
1684     }
1685     if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1686   }
1687
1688   // Repurpose a field for -c count
1689   TT.sortpos++;
1690   if (toys.optflags&(FLAG_n|FLAG_o)) {
1691     long long ll = tb->slot[SLOT_starttime];
1692
1693     if (toys.optflags&FLAG_o) ll *= -1;
1694     if (TT.time && TT.time>ll) return;
1695     TT.time = ll;
1696     free(TT.pgrep.snapshot);
1697     TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1698   } else do_pgk(tb);
1699 }
1700
1701 static int pgrep_match_process(long long *slot)
1702 {
1703   int match = shared_match_process(slot);
1704
1705   return (toys.optflags&FLAG_v) ? !match : match;
1706 }
1707
1708 void pgrep_main(void)
1709 {
1710   char **arg;
1711   struct regex_list *reg;
1712
1713   TT.pgrep.self = getpid();
1714
1715   // No signal names start with "L", so no need for "L: " parsing.
1716   if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1717     error_exit("bad -L '%s'", TT.pgrep.L);
1718
1719   comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1720   comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1721   comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1722   comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1723   comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1724   comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1725   comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1726
1727   if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1728       !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1729     if (!toys.optc) help_exit("No PATTERN");
1730
1731   if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1732   for (arg = toys.optargs; *arg; arg++) {
1733     reg = xmalloc(sizeof(struct regex_list));
1734     xregcomp(&reg->reg, *arg, REG_EXTENDED);
1735     reg->next = TT.pgrep.regexes;
1736     TT.pgrep.regexes = reg;
1737   }
1738   TT.match_process = pgrep_match_process;
1739   TT.show_process = (void *)match_pgrep;
1740
1741   dirtree_read("/proc", get_ps);
1742   if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1743   if (TT.pgrep.snapshot) {
1744     do_pgk(TT.pgrep.snapshot);
1745     if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1746   }
1747   if (TT.pgrep.d) xputc('\n');
1748 }
1749
1750 #define CLEANUP_pgrep
1751 #define FOR_pkill
1752 #include "generated/flags.h"
1753
1754 void pkill_main(void)
1755 {
1756   char **args = toys.optargs;
1757
1758   if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1759   if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1760   if (toys.optflags & FLAG_V) TT.tty = 1;
1761   pgrep_main();
1762 }