OSDN Git Service

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