OSDN Git Service

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