OSDN Git Service

Make cursor left/right change sort type in iotop.
[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  * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
33  * TODO: switch -fl to -y
34  * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
35  * TODO: iotop: TID PRIO USER DISK_READ DISK_WRITE SWAPIN IO% COMMAND
36  *       DISK_READ in B/s, K/s..., length 11
37  *       Total DISK READ: | Total DISK WRITE:
38
39 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflno*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN))
40 USE_TTOP(NEWTOY(ttop, ">0d#=3n#<1mb", TOYFLAG_USR|TOYFLAG_BIN))
41 USE_IOTOP(NEWTOY(iotop, "Aabkoqp*u*d#n#", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
42
43 config PS
44   bool "ps"
45   default y
46   help
47     usage: ps [-AadeflnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
48
49     List processes.
50
51     Which processes to show (selections may be comma separated lists):
52
53     -A  All processes
54     -a  Processes with terminals that aren't session leaders
55     -d  All processes that aren't session leaders
56     -e  Same as -A
57     -g  Belonging to GROUPs
58     -G  Belonging to real GROUPs (before sgid)
59     -p  PIDs (--pid)
60     -P  Parent PIDs (--ppid)
61     -s  In session IDs
62     -t  Attached to selected TTYs
63     -u  Owned by USERs
64     -U  Owned by real USERs (before suid)
65
66     Output modifiers:
67
68     -k  Sort FIELDs in +increasing or -decreasting order (--sort)
69     -n  Show numeric USER and GROUP
70     -w  Wide output (don't truncate at terminal width)
71
72     Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
73
74     -f  Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
75     -l  Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
76     -o  Output the listed FIELDs, each with optional :size and/or =title
77     -Z  Include LABEL
78
79     Available -o FIELDs:
80
81       ADDR    Instruction pointer
82       ARGS    Command line (argv[0-X] minus path)
83       CMD     COMM without -f, ARGS with -f
84       CMDLINE Command line (argv[0-X])
85       COMM    Original command name
86       COMMAND Original command path
87       CPU     Which processor running on
88       ETIME   Elapsed time since process start
89       F       Flags (1=FORKNOEXEC, 4=SUPERPRIV)
90       GID     Group id
91       GROUP   Group name
92       LABEL   Security label
93       MAJFL   Major page faults
94       MINFL   Minor page faults
95       NAME    Command name (argv[0])
96       NI      Niceness (lower is faster)
97       PCPU    Percentage of CPU time used
98       PGID    Process Group ID
99       PID     Process ID
100       PPID    Parent Process ID
101       PRI     Priority (higher is faster)
102       PSR     Processor last executed on
103       RGID    Real (before sgid) group ID
104       RGROUP  Real (before sgid) group name
105       RSS     Resident Set Size (memory in use, 4k pages)
106       RTPRIO  Realtime priority
107       RUID    Real (before suid) user ID
108       RUSER   Real (before suid) user name
109       S       Process state:
110               R (running) S (sleeping) D (device I/O) T (stopped)  t (traced)
111               Z (zombie)  X (deader)   x (dead)       K (wakekill) W (waking)
112       SCHED   Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
113       STAT    Process state (S) plus:
114               < high priority          N low priority L locked memory
115               s session leader         + foreground   l multithreaded
116       STIME   Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
117       SZ      Memory Size (4k pages needed to completely swap out process)
118       TIME    CPU time consumed
119       TTY     Controlling terminal
120       UID     User id
121       USER    User name
122       VSZ     Virtual memory size (1k units)
123       WCHAN   Waiting in kernel for
124
125       You can put a % after SZ, RSS
126       to get percentage
127
128 config TTOP
129   bool "ttop"
130   default n
131   help
132     usage: ttop [-mb] [ -d seconds ] [ -n iterations ]
133
134     todo: implement top
135
136     Provide a view of process activity in real time.
137     Keys
138        N/M/P/T show CPU usage, sort by pid/mem/cpu/time
139        S       show memory
140        R       reverse sort
141        H       toggle threads
142        C,1     toggle SMP
143        Q,^C    exit
144
145     Options
146        -n Iterations before exiting
147        -d Delay between updates
148        -m Same as 's' key
149        -b Batch mode
150
151 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
152 config IOTOP
153   bool "iotop"
154   default y
155   help
156     usage: iotop [-Aabkoq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
157
158     Rank processes by I/O. Cursor left/right to change sort, Q to exit.
159
160     -A  All I/O, not just disk
161     -a  Accumulated I/O (not percentage)
162     -b  Batch mode (no tty)
163     -d  Delay SECONDS between each cycle (default 3)
164     -k  Kilobytes
165     -n  Exit after NUMBER iterations
166     -o  Only show processes doing I/O
167     -p  Show these PIDs
168     -q  Quiet (no header lines)
169     -u  Show these USERs
170 */
171
172 #define FOR_ps
173 #include "toys.h"
174
175 GLOBALS(
176   union {
177     struct {
178       struct arg_list *G;
179       struct arg_list *g;
180       struct arg_list *U;
181       struct arg_list *u;
182       struct arg_list *t;
183       struct arg_list *s;
184       struct arg_list *p;
185       struct arg_list *o;
186       struct arg_list *P;
187       struct arg_list *k;
188     } ps;
189     struct {
190       long n;
191       long d;
192     } ttop;
193     struct {
194       long n;
195       long d;
196       struct arg_list *u;
197       struct arg_list *p;
198     } iotop;
199   };
200
201   struct sysinfo si;
202   struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
203   unsigned width, height;
204   dev_t tty;
205   void *fields, *kfields;
206   long long ticks, bits, ioread, iowrite, aioread, aiowrite;
207   size_t header_len;
208   int kcount, ksave, forcek, sortpos;
209   int (*match_process)(long long *slot);
210 )
211
212 struct strawberry {
213   struct strawberry *next, *prev;
214   short which, len, reverse;
215   char *title;
216   char forever[];
217 };
218
219 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
220  * table 1-4) but we shift and repurpose fields, with the result being:
221  *
222  * 0  pid         process id                1  ppid        parent process id
223  * 2  pgrp        process group             3  sid         session id
224  * 4  tty_nr      tty the process uses      5  tty_pgrp    pgrp of the tty
225  * 6  flags       task flags                7  min_flt     minor faults
226  * 8  cmin_flt    minor faults+child        9  maj_flt     major faults
227  * 10 cmaj_flt    major faults+child        11 utime       user+kernel jiffies
228  * 12 stime       kernel mode jiffies       13 cutime      user jiffies+child
229  * 14 cstime      kernel mode jiffies+child 15 priority    priority level
230  * 16 nice        nice level                17 num_threads number of threads
231  * 18 vmlck       locked memory             19 start_time  jiffies after boot
232  * 20 vsize       virtual memory size       21 rss         resident set size
233  * 22 rsslim      limit in bytes on rss     23 start_code  code segment addr
234  * 24 end_code    code segment address      25 start_stack stack address
235  * 26 esp         current value of ESP      27 eip         current value of EIP
236  * 28 iobytes     All I/O bytes             29 diobytes    disk I/O bytes
237  * 30 sigign      bitmap of ignored signals 31 uid         user id
238  * 32 ruid        real user id              33 gid         group id
239  * 34 rgid        real group id             35 exit_signal sent to parent thread
240  * 36 task_cpu    CPU task is scheduled on  37 rt_priority realtime priority
241  * 38 policy      man sched_setscheduler    39 blkio_ticks spent wait block IO
242  * 40 gtime       guest jiffies of task     41 cgtime      guest jiff of child
243  * 42 start_data  program data+bss address  43 end_data    program data+bss
244  * 44 upticks     46-19 (divisor for %)     45 argv0len    argv[0] length
245  * 46 uptime      sysinfo.uptime @read time 47 vsz         Virtual Size
246  * 48 rss         Resident Set Size         49 shr         Shared memory
247  * 50 rchar       All bytes read            51 wchar       All bytes written
248  * 52 rbytes      Disk bytes read           53 rbytes      Disk bytes written
249  * 54 swap        Swap pages used
250  */
251
252 // Data layout in toybuf
253 struct carveup {
254   long long slot[55];       // data from /proc
255   unsigned short offset[5]; // offset of fields in str[] (skip name, always 0)
256   char state;
257   char str[];               // name, tty, command, wchan, attr, cmdline
258 };
259
260 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
261 // 64|slot means compare as string when sorting
262 struct typography {
263   char *name;
264   signed char width, slot;
265 } static const typos[] = TAGGED_ARRAY(PS,
266   // stat#s: PID PPID PRI NI ADDR SZ RSS PGID VSZ MAJFL MINFL PR PSR RTPRIO
267   // SCHED
268   {"PID", 5, 0}, {"PPID", 5, 1}, {"PRI", 3, 15}, {"NI", 3, 16},
269   {"ADDR", 4+sizeof(long), 27}, {"SZ", 5, 20}, {"RSS", 5, 21}, {"PGID", 5, 2},
270   {"VSZ", 6, 20}, {"MAJFL", 6, 9}, {"MINFL", 6, 7}, {"PR", 2, 15},
271   {"PSR", 3, 36}, {"RTPRIO", 6, 37}, {"SCH", 3, 38},
272
273   // user/group: UID USER RUID RUSER GID GROUP RGID RGROUP
274   {"UID", 5, 31}, {"USER", -8, 64|31}, {"RUID", 4, 32}, {"RUSER", -8, 64|32},
275   {"GID", 8, 33}, {"GROUP", -8, 64|33}, {"RGID", 4, 34}, {"RGROUP", -8, 64|34},
276
277   // CMD TTY WCHAN LABEL CMDLINE COMMAND
278   {"COMM", -15, -1}, {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4},
279   {"COMMAND", -27, -5}, {"CMDLINE", -27, -6}, {"ARGS", -27, -6},
280   {"NAME", -15, -6}, {"CMD", -27, -1},
281
282   // TIME ELAPSED TIME+
283   {"TIME", 8, 11}, {"ELAPSED", 11, 19}, {"TIME+", 9, 11},
284
285   // Remaining ungrouped
286   {"STIME", 5, 19}, {"F", 1, 64|6}, {"S", -1, 64}, {"C", 1, 0}, {"%CPU", 4, 64},
287   {"STAT", -5, 64}, {"%VSZ", 5, 23}, {"VIRT", 4, 47}, {"RES", 4, 48},
288   {"SHR", 4, 49}, {"READ", 6, 50}, {"WRITE", 6, 51}, {"IO", 6, 28},
289   {"DREAD", 6, 52}, {"DWRITE", 6, 53}, {"SWAP", 6, 54}, {"DIO", 6, 29}
290 );
291
292 // Return 0 to discard, nonzero to keep
293 static int shared_match_process(long long *slot)
294 {
295   struct ptr_len match[] = {
296     {&TT.gg, 33}, {&TT.GG, 34}, {&TT.pp, 0}, {&TT.PP, 1}, {&TT.ss, 3},
297     {&TT.tt, 4}, {&TT.uu, 31}, {&TT.UU, 32}
298   };
299   int i, j;
300   long *ll = 0;
301
302   // Do we have -g -G -p -P -s -t -u -U options selecting processes?
303   for (i = 0; i < ARRAY_LEN(match); i++) {
304     struct ptr_len *mm = match[i].ptr;
305     if (mm->len) {
306       ll = mm->ptr;
307       for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
308     }
309   }
310
311   return ll ? 0 : -1;
312 }
313
314
315 // Return 0 to discard, nonzero to keep
316 static int ps_match_process(long long *slot)
317 {
318   int i = shared_match_process(slot);
319
320   if (i>0) return 1;
321   // If we had selections and didn't match them, don't display
322   if (!i) return 0;
323
324   // Filter implicit categories for other display types
325   if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[3]==*slot) return 0;
326   if ((toys.optflags&FLAG_a) && !slot[4]) return 0;
327   if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e)) && TT.tty!=slot[4])
328     return 0;
329
330   return 1;
331 }
332
333 // Convert field to string representation
334 static char *string_field(struct carveup *tb, struct strawberry *field)
335 {
336   char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
337   int which = field->which, sl = typos[which].slot;
338   long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
339
340   // Default: unsupported (5 "C")
341   sprintf(out, "-");
342
343   // stat#s: PID PPID PRI NI ADDR SZ RSS PGID VSZ MAJFL MINFL PR PSR RTPRIO SCH
344   if (which <= PS_SCH) {
345     char *fmt = "%lld";
346
347     if (which==PS_PRI) ll = 39-ll;
348     if (which==PS_ADDR) fmt = "%llx";
349     else if (which==PS_SZ) ll >>= 12;
350     else if (which==PS_RSS) ll <<= 2;
351     else if (which==PS_VSZ) ll >>= 10;
352     else if (which==PS_PR && ll<-9) fmt="RT";
353     else if (which==PS_RTPRIO && ll == 0) fmt="-";
354     sprintf(out, fmt, ll);
355
356   // user/group: UID USER RUID RUSER GID GROUP RGID RGROUP
357   } else if (which <= PS_RGROUP) {
358     sprintf(out, "%lld", ll);
359     if (sl&64) {
360       if (which > PS_RUSER) {
361         struct group *gr = getgrgid(ll);
362
363         if (gr) out = gr->gr_name;
364       } else {
365         struct passwd *pw = getpwuid(ll);
366
367         if (pw) out = pw->pw_name;
368       }
369     }
370
371   // COMM TTY WCHAN LABEL COMMAND CMDLINE ARGS NAME CMD
372   } else if (sl < 0) {
373     if (slot[45])
374       tb->str[tb->offset[4]+slot[45]] = (which == PS_NAME) ? 0 : ' ';
375     out = tb->str;
376     sl *= -1;
377     if (--sl) out += tb->offset[--sl];
378     if (which==PS_ARGS)
379       for (s = out; *s && *s != ' '; s++) if (*s == '/') out = s+1;
380     if (which>=PS_COMMAND && !*out) sprintf(out = buf, "[%s]", tb->str);
381   // TIME ELAPSED TIME+
382   } else if (which <= PS_TIME_) {
383     int unit = 60, pad = 2, j = TT.ticks; 
384     time_t seconds;
385
386     if (which!=PS_TIME_) unit *= 60*24;
387     else pad = 0;
388     // top adjusts slot[44], we want original meaning.
389     if (which==PS_ELAPSED) ll = (slot[46]*j)-slot[19];
390     seconds = ll/j;
391
392     // Output days-hours:mins:secs, skipping non-required fields with zero
393     // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
394     for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
395       if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
396       if (s) {
397         s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
398         pad = 2;
399         if ((*s = "-::"[j])) s++;
400       }
401       seconds %= unit;
402       unit /= j ? 60 : 24;
403     }
404     if (which==PS_TIME_ && s-out<8)
405       sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
406
407   // Posix doesn't specify what flags should say. Man page says
408   // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
409   } else if (which==PS_F) sprintf(out, "%llo", (slot[6]>>6)&5);
410   else if (which==PS_S || which==PS_STAT) {
411     s = out;
412     *s++ = tb->state;
413     if (which==PS_STAT) {
414       // TODO l = multithreaded
415       if (slot[16]<0) *s++ = '<';
416       else if (slot[16]>0) *s++ = 'N';
417       if (slot[3]==*slot) *s++ = 's';
418       if (slot[18]) *s++ = 'L';
419       if (slot[5]==*slot) *s++ = '+';
420     } 
421     *s = 0;
422   } else if (which==PS_STIME) {
423     time_t t = time(0)-slot[46]+slot[19]/TT.ticks;
424
425     // Padding behavior's a bit odd: default field size is just hh:mm.
426     // Increasing stime:size reveals more data at left until full,
427     // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
428     // then add :ss on right for :19.
429     strftime(out, 260, "%F %T", localtime(&t));
430     out = out+strlen(out)-3-abs(field->len);
431     if (out<buf) out = buf;
432   } else if (which==PS__CPU || which==PS__VSZ) {
433     if (which==PS__CPU) sl = (slot[11]*1000)/slot[44];
434     else sl = (slot[23]*1000)/TT.si.totalram;
435     sprintf(out, "%d.%d", sl/10, sl%10);
436   } else if (which>=PS_VIRT && which <= PS_DIO) {
437     ll = slot[typos[which].slot];
438     if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
439     if (TT.forcek) sprintf(out, "%lldk", ll/1024);
440     else human_readable(out, ll, 0);
441   }
442
443   return out;
444 }
445
446 // Display process data that get_ps() read from /proc, formatting with TT.fields
447 static void show_ps(struct carveup *tb)
448 {
449   struct strawberry *field;
450   int i, len, width = TT.width;
451
452   // Loop through fields to display
453   for (field = TT.fields; field; field = field->next) {
454     char *out = string_field(tb, field);
455
456     // Output the field, appropriately padded
457     len = width - (field != TT.fields);
458     if (!field->next && field->len<0) i = 0;
459     else {
460       i = len<abs(field->len) ? len : field->len;
461       len = abs(i);
462     }
463
464     // TODO test utf8 fontmetrics
465     width -= printf(" %*.*s" + (field == TT.fields), i, len, out);
466     if (!width) break;
467   }
468   xputc('\n');
469 }
470
471 // dirtree callback: read data about process to display, store, or discard it.
472 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
473 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
474 static int get_ps(struct dirtree *new)
475 {
476   struct {
477     char *name;
478     long long bits;
479   } fetch[] = {
480     {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
481     {"exe", _PS_COMMAND}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}
482   };
483   struct carveup *tb = (void *)toybuf;
484   long long *slot = tb->slot;
485   char *name, *s, *buf = tb->str, *end = 0;
486   int i, j, fd;
487   off_t len;
488
489   // Recurse one level into /proc children, skip non-numeric entries
490   if (!new->parent) return DIRTREE_RECURSE|DIRTREE_SHUTUP|TT.ksave;
491
492   memset(slot, 0, sizeof(tb->slot));
493   if (!(*slot = atol(new->name))) return 0;
494   fd = dirtree_parentfd(new);
495
496   len = 2048;
497   sprintf(buf, "%lld/stat", *slot);
498   if (!readfileat(fd, buf, buf, &len)) return 0;
499
500   // parse oddball fields (name and state). Name can have embedded ')' so match
501   // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
502   // All remaining fields should be numeric.
503   if (!(name = strchr(buf, '('))) return 0;
504   for (s = ++name; *s; s++) if (*s == ')') end = s;
505   if (!end || end-name>255) return 0;
506
507   // Parse numeric fields (starting at 4th field in slot[1])
508   if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
509   for (j = 1; j<50; j++) if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
510
511   // Now we've read the data, move status and name right after slot[] array,
512   // and convert low chars to ? while we're at it.
513   for (i = 0; i<end-name; i++)
514     if ((tb->str[i] = name[i]) < ' ') tb->str[i] = '?';
515   buf = tb->str+i;
516   *buf++ = 0;
517   len = sizeof(toybuf)-(buf-toybuf);
518
519   // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
520   // or numeric wchan, and the remaining two are always zero), and vmlck into
521   // 18 (which is "obsolete, always 0" from stat)
522   slot[31] = new->st.st_uid;
523   slot[33] = new->st.st_gid;
524
525   // TIME and TIME+ use combined value, ksort needs 'em added.
526   slot[11] += slot[12];
527
528   // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
529   // and save ruid, rgid, and vmlck.
530   if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
531                |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
532   {
533     off_t temp = len;
534
535     sprintf(buf, "%lld/status", *slot);
536     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
537     s = strafter(buf, "\nUid:");
538     slot[32] = s ? atol(s) : new->st.st_uid;
539     s = strafter(buf, "\nGid:");
540     slot[34] = s ? atol(s) : new->st.st_gid;
541     if ((s = strafter(buf, "\nVmLck:"))) slot[18] = atoll(s);
542     if ((s = strafter(buf, "\nVmSwap:"))) slot[54] = atoll(s);
543   }
544
545   // Do we need to read "io"?
546   if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
547     off_t temp = len;
548
549     sprintf(buf, "%lld/io", *slot);
550     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
551     if ((s = strafter(buf, "rchar:"))) slot[50] = atoll(s);
552     if ((s = strafter(buf, "wchar:"))) slot[51] = atoll(s);
553     if ((s = strafter(buf, "read_bytes:"))) slot[52] = atoll(s);
554     if ((s = strafter(buf, "write_bytes:"))) slot[53] = atoll(s);
555     slot[28] = slot[50]+slot[51]+slot[54];
556     slot[29] = slot[52]+slot[53]+slot[54];
557   }
558
559   // We now know enough to skip processes we don't care about.
560   if (TT.match_process && !TT.match_process(slot)) return 0;
561
562   // /proc data is generated as it's read, so for maximum accuracy on slow
563   // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
564   sysinfo(&TT.si);
565   slot[44] = ((slot[46] = TT.si.uptime)*TT.ticks) - slot[19];
566
567   // Do we need to read "statm"?
568   if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
569     off_t temp = len;
570
571     sprintf(buf, "%lld/statm", *slot);
572     if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
573     
574     for (s = buf, i=0; i<3; i++)
575       if (!sscanf(s, " %lld%n", slot+47+i, &j)) slot[47+i] = 0;
576       else s += j;
577   }
578
579   // Fetch string data while parentfd still available, appending to buf.
580   // (There's well over 3k of toybuf left. We could dynamically malloc, but
581   // it'd almost never get used, querying length of a proc file is awkward,
582   // fixed buffer is nommu friendly... Wait for somebody to complain. :)
583   slot[45] = 0;
584   for (j = 0; j<ARRAY_LEN(fetch); j++) { 
585     tb->offset[j] = buf-(tb->str);
586     if (!(TT.bits&fetch[j].bits)) {
587       *buf++ = 0;
588       continue;
589     }
590
591     // Determine remaining space, reserving minimum of 256 bytes/field and
592     // 260 bytes scratch space at the end (for output conversion later).
593     len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
594     sprintf(buf, "%lld/%s", *slot, fetch[j].name);
595
596     // For cmdline we readlink instead of read contents
597     if (j==3) {
598       if ((len = readlinkat(fd, buf, buf, len))>0) buf[len] = 0;
599       else *buf = 0;
600
601     // If it's not the TTY field, data we want is in a file.
602     // Last length saved in slot[] is command line (which has embedded NULs)
603     } else if (!j) {
604       int rdev = slot[4];
605       struct stat st;
606
607       // Call no tty "?" rather than "0:0".
608       strcpy(buf, "?");
609       if (rdev) {
610         // Can we readlink() our way to a name?
611         for (i = 0; i<3; i++) {
612           sprintf(buf, "%lld/fd/%i", *slot, i);
613           if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
614             && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
615           {
616             buf[len] = 0;
617             break;
618           }
619         }
620
621         // Couldn't find it, try all the tty drivers.
622         if (i == 3) {
623           FILE *fp = fopen("/proc/tty/drivers", "r");
624           int tty_major = 0, maj = major(rdev), min = minor(rdev);
625
626           if (fp) {
627             while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
628               // TODO: we could parse the minor range too.
629               if (tty_major == maj) {
630                 sprintf(buf+strlen(buf), "%d", min);
631                 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
632                   break;
633               }
634               tty_major = 0;
635             }
636             fclose(fp);
637           }
638
639           // Really couldn't find it, so just show major:minor.
640           if (!tty_major) sprintf(buf, "%d:%d", maj, min);
641         }
642
643         s = buf;
644         if (strstart(&s, "/dev/")) memmove(buf, s, strlen(s)+1);
645       }
646
647     // Data we want is in a file.
648     // Last length saved in slot[] is command line (which has embedded NULs)
649     } else {
650
651       // When command has no arguments, don't space over the NUL
652       if (readfileat(fd, buf, buf, &len) && len>0) {
653         int temp = 0;
654
655         if (buf[len-1]=='\n') buf[--len] = 0;
656
657         // Turn NUL to space, other low ascii to ?
658         for (i=0; i<len; i++) {
659           char c = buf[i];
660
661           if (!c) {
662             if (!temp) temp = i;
663             c = ' ';
664           } else if (c<' ') c = '?';
665           buf[i] = c;
666         }
667         len = temp; // position of _first_ NUL
668       } else *buf = len = 0;
669       // Store end of argv[0] so NAME and CMDLINE can differ.
670       slot[45] = len;
671     }
672
673     buf += strlen(buf)+1;
674   }
675
676   // If we need to sort the output, add it to the list and return.
677   if (TT.ksave) {
678     s = xmalloc(buf-toybuf);
679     new->extra = (long)s;
680     memcpy(s, toybuf, buf-toybuf);
681     TT.kcount++;
682
683   // Otherwise display it now
684   } else show_ps(tb);
685
686   return TT.ksave;
687 }
688
689 static char *parse_ko(void *data, char *type, int length)
690 {
691   struct strawberry *field;
692   char *width, *title, *end, *s;
693   int i, j, k;
694
695   // Get title, length of title, type, end of type, and display width
696
697   // Chip off =name to display
698   if ((end = strchr(type, '=')) && length>(end-type)) {
699     title = end+1;
700     length -= (end-type)+1;
701   } else {
702     end = type+length;
703     title = 0;
704   }
705
706   // Chip off :width to display
707   if ((width = strchr(type, ':')) && width<end) {
708     if (!title) length = width-type;
709   } else width = 0;
710
711   // Allocate structure, copy title
712   field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
713   if (title) {
714     memcpy(field->title = field->forever, title, length);
715     field->title[field->len = length] = 0;
716   }
717
718   if (width) {
719     field->len = strtol(++width, &title, 10);
720     if (!isdigit(*width) || title != end) return title;
721     end = --width;
722   }
723
724   // Find type
725   if (*(struct strawberry **)data == TT.kfields) {
726     field->reverse = 1;
727     if (*type == '-') field->reverse = -1;
728     else if (*type != '+') type--;
729     type++;
730   }
731   for (i = 0; i<ARRAY_LEN(typos); i++) {
732     field->which = i;
733     for (j = 0; j<2; j++) {
734       if (!j) s = typos[i].name;
735       // posix requires alternate names for some fields
736       else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
737         PS_VSZ, PS_USER, 0}, i))) continue;
738       else
739         s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
740
741       if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
742     }
743     if (j!=2) break;
744   }
745   if (i==ARRAY_LEN(typos)) return type;
746   if (!field->title) field->title = typos[field->which].name;
747   if (!field->len) field->len = typos[field->which].width;
748   else if (typos[field->which].width<0) field->len *= -1;
749   dlist_add_nomalloc(data, (void *)field);
750
751   // Print padded header for -o.
752   if (*(struct strawberry **)data == TT.fields) {
753     TT.header_len +=
754       snprintf(toybuf + TT.header_len, sizeof(toybuf) - TT.header_len,
755                " %*s" + (field == TT.fields), field->len, field->title);
756     TT.bits |= 1LL<<field->which;
757   }
758
759   return 0;
760 }
761
762 // Parse -p -s -t -u -U -g -G
763 static char *parse_rest(void *data, char *str, int len)
764 {
765   struct ptr_len *pl = (struct ptr_len *)data;
766   long *ll = pl->ptr;
767   char *end;
768   int num = 0;
769
770   // numeric: -p, -s
771   // gg, GG, pp, ss, tt, uu, UU, *parsing;
772  
773   // Allocate next chunk of data
774   if (!(15&pl->len))
775     ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
776
777   // Parse numerical input
778   if (isdigit(*str)) {
779     ll[pl->len] = xstrtol(str, &end, 10);
780     if (end==(len+str)) num++;
781   }
782
783   if (pl==&TT.pp || pl==&TT.ss) {
784     if (num && ll[pl->len]>0) {
785       pl->len++;
786
787       return 0;
788     }
789   } else if (pl==&TT.tt) {
790     // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
791     if (!num) {
792       if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
793       if (strstart(&str, "pts/")) {
794         len -= 4;
795         num++;
796       } else if (strstart(&str, "tty")) len -= 3;
797     }
798     if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
799       struct stat st;
800
801       end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
802       memcpy(end, str, len);
803       end[len] = 0;
804       xstat(toybuf, &st);
805       ll[pl->len++] = st.st_rdev;
806
807       return 0;
808     }
809   } else if (len<255) {
810     char name[256];
811
812     if (num) {
813       pl->len++;
814
815       return 0;
816     }
817
818     memcpy(name, str, len);
819     name[len] = 0;
820     if (pl==&TT.gg || pl==&TT.GG) {
821       struct group *gr = getgrnam(name);
822       if (gr) {
823         ll[pl->len++] = gr->gr_gid;
824
825         return 0;
826       }
827     } else if (pl==&TT.uu || pl==&TT.UU) {
828       struct passwd *pw = getpwnam(name);
829       if (pw) {
830         ll[pl->len++] = pw->pw_uid;
831
832         return 0;
833       }
834     }
835   }
836
837   // Return error
838   return str;
839 }
840
841 // sort for -k
842 static int ksort(void *aa, void *bb)
843 {
844   struct strawberry *field;
845   struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
846   int ret = 0, slot;
847
848   for (field = TT.kfields; field; field = field->next) {
849     slot = typos[field->which].slot;
850     // Compare as strings?
851     if (slot&64) {
852       memccpy(toybuf, string_field(ta, field), 0, 2048);
853       toybuf[2048] = 0;
854       ret = strcmp(toybuf, string_field(tb, field));
855     } else {
856       if (ta->slot[slot]<tb->slot[slot]) ret = -1;
857       if (ta->slot[slot]>tb->slot[slot]) ret = 1;
858     }
859
860     if (ret) return ret*field->reverse;
861   }
862
863   return 0;
864 }
865
866 static struct carveup **collate(int count, struct dirtree *dt,
867   int (*sort)(void *a, void *b))
868 {
869   struct dirtree *temp;
870   struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
871   int i;
872
873   // descend into child list
874   *tbsort = (void *)dt;
875   dt = dt->child;
876   free(*tbsort);
877
878   // populate array
879   for (i = 0; i < count; i++) {
880     temp = dt->next;
881     tbsort[i] = (void *)dt->extra;
882     free(dt);
883     dt = temp;
884   }
885
886   return tbsort;
887
888
889 static void shared_main(void)
890 {
891   int i;
892
893   TT.ticks = sysconf(_SC_CLK_TCK);
894   if (!TT.width) {
895     TT.width = 80;
896     TT.height = 25;
897     terminal_size(&TT.width, &TT.height);
898   }
899
900   // find controlling tty, falling back to /dev/tty if none
901   for (i = 0; !TT.tty && i<4; i++) {
902     struct stat st;
903     int fd = i;
904
905     if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
906
907     if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
908     if (i==3) close(fd);
909   }
910 }
911
912 void ps_main(void)
913 {
914   struct dirtree *dt;
915   int i;
916
917   if (toys.optflags&FLAG_w) TT.width = 99999;
918   shared_main();
919
920   // parse command line options other than -o
921   comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
922   comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
923   comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
924   comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
925   comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
926   comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
927   comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
928   comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
929   comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
930   dlist_terminate(TT.kfields);
931
932   // Parse manual field selection, or default/-f/-l, plus -Z,
933   // constructing the header line in toybuf as we go.
934   if (toys.optflags&FLAG_Z) {
935     struct arg_list Z = { 0, "LABEL" };
936
937     comma_args(&Z, &TT.fields, "-Z", parse_ko);
938   }
939   if (TT.ps.o) comma_args(TT.ps.o, &TT.fields, "bad -o field", parse_ko);
940   else {
941     struct arg_list al;
942
943     al.next = 0;
944     if (toys.optflags&FLAG_f)
945       al.arg = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
946     else if (toys.optflags&FLAG_l)
947       al.arg = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
948     else if (CFG_TOYBOX_ON_ANDROID)
949       al.arg = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,CMDLINE";
950     else al.arg = "PID,TTY,TIME,CMD";
951
952     comma_args(&al, &TT.fields, 0, parse_ko);
953   }
954   dlist_terminate(TT.fields);
955   printf("%s\n", toybuf);
956
957   // misunderstand fields the flags say to
958   if (toys.optflags&(FLAG_f|FLAG_n)) {
959     struct strawberry *ever;
960
961     for (ever = TT.fields; ever; ever = ever->next) {
962       int alluc = ever->which;
963
964       if ((toys.optflags&FLAG_f) && alluc==PS_CMD) alluc = PS_ARGS;
965       if ((toys.optflags&FLAG_n) && alluc>=PS_UID && alluc<=PS_RGROUP
966           && (typos[alluc].slot&64)) alluc--;
967       if (alluc != ever->which) {
968         TT.bits &= 1LL<<ever->which;
969         TT.bits |= 1LL<<(ever->which = alluc);
970       }
971     }
972   }
973
974   TT.ksave = DIRTREE_SAVE*!!(toys.optflags&FLAG_k);
975   TT.match_process = ps_match_process;
976   dt = dirtree_read("/proc", get_ps);
977
978   if (toys.optflags&FLAG_k) {
979     struct carveup **tbsort = collate(TT.kcount, dt, ksort);
980
981     qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
982     for (i = 0; i<TT.kcount; i++) {
983       show_ps(tbsort[i]);
984       free(tbsort[i]);
985     }
986     if (CFG_TOYBOX_FREE) free(tbsort);
987   }
988
989   if (CFG_TOYBOX_FREE) {
990     free(TT.gg.ptr);
991     free(TT.GG.ptr);
992     free(TT.pp.ptr);
993     free(TT.PP.ptr);
994     free(TT.ss.ptr);
995     free(TT.tt.ptr);
996     free(TT.uu.ptr);
997     free(TT.UU.ptr);
998     llist_traverse(TT.fields, free);
999   }
1000 }
1001
1002 #define CLEANUP_ps
1003 #define FOR_ttop
1004 #include "generated/flags.h"
1005
1006 void ttop_main(void)
1007 {
1008   printf("hello world\n");
1009 }
1010
1011 #define CLEANUP_ttop
1012 #define FOR_iotop
1013 #include "generated/flags.h"
1014
1015 // select which of the -o fields to sort by
1016 static void setsort(int pos)
1017 {
1018   struct strawberry *field, *going2;
1019   int i = 0;
1020
1021   if (pos<0) pos = 0;
1022
1023   for (field = TT.fields; field; field = field->next) {
1024     if ((TT.sortpos = i++)<pos) continue;
1025     going2 = TT.kfields;
1026     going2->which = field->which;
1027     going2->len = field->len;
1028     break;
1029   }
1030 }
1031
1032 void iotop_main(void)
1033 {
1034   struct timespec ts;
1035   long long timeout = 0, now;
1036   struct proclist {
1037     struct carveup **tb;
1038     int count;
1039   } plist[2], *plold, *plnew, old, new, mix;
1040   struct arg_list al;
1041   char *d = "D"+!!(toys.optflags&FLAG_A), *header, scratch[16],
1042             deltas[] = {11,28,29,44,50,51,52,53,54};
1043   unsigned tock = 0;
1044   int i, lines, done = 0;
1045
1046   if (!TT.iotop.d) TT.iotop.d = 3;
1047   TT.iotop.d *= 1000;
1048   if (toys.optflags&FLAG_k) TT.forcek++;
1049   if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1050   else {
1051     xset_terminal(0, 1, 0);
1052     sigatexit(tty_sigreset);
1053   }
1054   shared_main();
1055
1056   // TODO: usage: iotop [-oq]
1057
1058   comma_args(TT.iotop.u, &TT.uu, "bad -u", parse_rest);
1059   comma_args(TT.iotop.p, &TT.pp, "bad -p", parse_rest);
1060
1061   al.next = 0;
1062   al.arg = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM", d, d, d);
1063   comma_args(&al, &TT.fields, 0, parse_ko);
1064   free(al.arg);
1065   dlist_terminate(TT.fields);
1066   header = strdup(toybuf);
1067
1068   // Fallback sorts. First (dummy) field gets overwritten by setsort()
1069   al.arg = xmprintf("-S,-%sIO,-ETIME,-PID",d);
1070   comma_args(&al, &TT.kfields, 0, parse_ko);
1071   free(al.arg);
1072   dlist_terminate(TT.kfields);
1073   setsort(6);
1074
1075   TT.ksave = DIRTREE_SAVE;
1076   TT.match_process = shared_match_process;
1077   memset(plist, 0, sizeof(plist));
1078   do {
1079     struct dirtree *dt = dirtree_read("/proc", get_ps);
1080
1081     plold = plist+(tock++&1);
1082     plnew = plist+(tock&1);
1083     plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
1084     TT.kcount = 0;
1085
1086     // First time, wait a quarter of a second to collect a little delta data.
1087     if (!plold->tb) {
1088       msleep(250);
1089       continue;
1090     }
1091
1092     // Collate old and new into "mix", depends on /proc read in pid sort order
1093     old = *plold;
1094     new = *plnew;
1095     mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1096     mix.count = 0;
1097
1098     while (old.count || new.count) {
1099       struct carveup *otb = *old.tb, *ntb = *new.tb;
1100
1101       // If we just have old, discard it.
1102       if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1103         old.tb++;
1104         old.count--;
1105
1106         continue;
1107       }
1108
1109       // If we just have new, use it verbatim
1110       if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1111       else {
1112
1113         // If we have both, adjust slot[deltas[]] to be relative to previous
1114         // measurement rather than process start. Stomping old.data is fine
1115         // because we free it after displaying.
1116         if (!(toys.optflags&FLAG_a))
1117           for (i = 0; i<ARRAY_LEN(deltas); i++)
1118             otb->slot[deltas[i]] = ntb->slot[deltas[i]] - otb->slot[deltas[i]];
1119         if (!(toys.optflags&FLAG_o) || otb->slot[28+!(toys.optflags&FLAG_A)]) {
1120           mix.tb[mix.count] = otb;
1121           mix.count++;
1122         }
1123         old.tb++;
1124         old.count--;
1125       }
1126       new.tb++;
1127       new.count--;
1128     }
1129
1130     // Will will re-fetch no data before its time. - Mork calling Orson Welles
1131     for (;;) {
1132       char was, is, *pos;
1133
1134       qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1135       if (!(toys.optflags&FLAG_b)) printf("\033[H\033[J");
1136       if (!(toys.optflags&FLAG_q)) {
1137         i = 0;
1138         strcpy(pos = toybuf, header);
1139         for (i=0, is = *pos; *pos; pos++) {
1140           was = is;
1141           is = *pos;
1142           if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
1143           if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1144         }
1145         printf("\033[7m%s\033[0m\n\r", toybuf);
1146
1147         if (!(toys.optflags&FLAG_b))
1148           terminal_probesize(&TT.width, &TT.height);
1149       }
1150       lines = TT.height-2;
1151
1152       for (i=0; i<lines && i<mix.count; i++) {
1153         show_ps(mix.tb[i]);
1154         xputc('\r');
1155       }
1156
1157       if (TT.iotop.n && !--TT.iotop.n) {
1158         done++;
1159         break;
1160       }
1161
1162       // Get current time in miliseconds
1163       clock_gettime(CLOCK_MONOTONIC, &ts);
1164       now = ts.tv_sec*1000+ts.tv_nsec/1000000;
1165       if (timeout<=now) timeout += TT.iotop.d;
1166       if (timeout<=now) timeout = now+TT.iotop.d;
1167
1168       i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1169       if (i==-1 || i==3 || toupper(i)=='Q') {
1170         done++;
1171         break;
1172       }
1173       if (i==-2) break;
1174
1175       // Flush unknown escape sequences.
1176       if (i==27) {
1177         while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1178         continue;
1179       }
1180
1181       i -= 256;
1182       if (i == KEY_LEFT) setsort(TT.sortpos-1);
1183       else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1184       else continue;
1185       break;
1186     }
1187
1188     free(mix.tb);
1189     for (i=0; i<plold->count; i++) free(plold->tb[i]);
1190     free(plold->tb);
1191   } while (!done);
1192   if (!(toys.optflags&FLAG_b)) tty_reset();
1193 }