OSDN Git Service

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