OSDN Git Service

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