OSDN Git Service

First pass at 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.
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;
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
850     slot = typos[field->which].slot;
851     // Compare as strings?
852     if (slot&64) {
853       memccpy(toybuf, string_field(ta, field), 0, 2048);
854       toybuf[2048] = 0;
855       ret = strcmp(toybuf, string_field(tb, field));
856     } else {
857       if (ta->slot[slot]<tb->slot[slot]) ret = -1;
858       if (ta->slot[slot]>tb->slot[slot]) ret = 1;
859     }
860
861     if (ret) return ret*field->reverse;
862   }
863
864   return 0;
865 }
866
867 static struct carveup **collate(int count, struct dirtree *dt,
868   int (*sort)(void *a, void *b))
869 {
870   struct dirtree *temp;
871   struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
872   int i;
873
874   // descend into child list
875   *tbsort = (void *)dt;
876   dt = dt->child;
877   free(*tbsort);
878
879   // populate array
880   for (i = 0; i < count; i++) {
881     temp = dt->next;
882     tbsort[i] = (void *)dt->extra;
883     free(dt);
884     dt = temp;
885   }
886
887
888   return tbsort;
889
890
891 static void shared_main(void)
892 {
893   int i;
894
895   TT.ticks = sysconf(_SC_CLK_TCK);
896   if (!TT.width) {
897     TT.width = 80;
898     TT.height = 25;
899     terminal_size(&TT.width, &TT.height);
900   }
901
902   // find controlling tty, falling back to /dev/tty if none
903   for (i = 0; !TT.tty && i<4; i++) {
904     struct stat st;
905     int fd = i;
906
907     if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
908
909     if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
910     if (i==3) close(fd);
911   }
912 }
913
914 void ps_main(void)
915 {
916   struct dirtree *dt;
917   int i;
918
919   if (toys.optflags&FLAG_w) TT.width = 99999;
920   shared_main();
921
922   // parse command line options other than -o
923   comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
924   comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
925   comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
926   comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
927   comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
928   comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
929   comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
930   comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
931   comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
932   dlist_terminate(TT.kfields);
933
934   // Parse manual field selection, or default/-f/-l, plus -Z,
935   // constructing the header line in toybuf as we go.
936   if (toys.optflags&FLAG_Z) {
937     struct arg_list Z = { 0, "LABEL" };
938
939     comma_args(&Z, &TT.fields, "-Z", parse_ko);
940   }
941   if (TT.ps.o) comma_args(TT.ps.o, &TT.fields, "bad -o field", parse_ko);
942   else {
943     struct arg_list al;
944
945     al.next = 0;
946     if (toys.optflags&FLAG_f)
947       al.arg = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
948     else if (toys.optflags&FLAG_l)
949       al.arg = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
950     else if (CFG_TOYBOX_ON_ANDROID)
951       al.arg = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,CMDLINE";
952     else al.arg = "PID,TTY,TIME,CMD";
953
954     comma_args(&al, &TT.fields, 0, parse_ko);
955   }
956   dlist_terminate(TT.fields);
957   printf("%s\n", toybuf);
958
959   // misunderstand fields the flags say to
960   if (toys.optflags&(FLAG_f|FLAG_n)) {
961     struct strawberry *ever;
962
963     for (ever = TT.fields; ever; ever = ever->next) {
964       int alluc = ever->which;
965
966       if ((toys.optflags&FLAG_f) && alluc==PS_CMD) alluc = PS_ARGS;
967       if ((toys.optflags&FLAG_n) && alluc>=PS_UID && alluc<=PS_RGROUP
968           && (typos[alluc].slot&64)) alluc--;
969       if (alluc != ever->which) {
970         TT.bits &= 1LL<<ever->which;
971         TT.bits |= 1LL<<(ever->which = alluc);
972       }
973     }
974   }
975
976   TT.ksave = DIRTREE_SAVE*!!(toys.optflags&FLAG_k);
977   TT.match_process = ps_match_process;
978   dt = dirtree_read("/proc", get_ps);
979
980   if (toys.optflags&FLAG_k) {
981     struct carveup **tbsort = collate(TT.kcount, dt, ksort);
982
983     qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
984     for (i = 0; i<TT.kcount; i++) {
985       show_ps(tbsort[i]);
986       free(tbsort[i]);
987     }
988     if (CFG_TOYBOX_FREE) free(tbsort);
989   }
990
991   if (CFG_TOYBOX_FREE) {
992     free(TT.gg.ptr);
993     free(TT.GG.ptr);
994     free(TT.pp.ptr);
995     free(TT.PP.ptr);
996     free(TT.ss.ptr);
997     free(TT.tt.ptr);
998     free(TT.uu.ptr);
999     free(TT.UU.ptr);
1000     llist_traverse(TT.fields, free);
1001   }
1002 }
1003
1004 #define CLEANUP_ps
1005 #define FOR_ttop
1006 #include "generated/flags.h"
1007
1008 void ttop_main(void)
1009 {
1010   printf("hello world\n");
1011 }
1012
1013 #define CLEANUP_ttop
1014 #define FOR_iotop
1015 #include "generated/flags.h"
1016
1017 void iotop_main(void)
1018 {
1019   struct proclist {
1020     struct carveup **tb;
1021     int count;
1022   } plist[2], *plold, *plnew, old, new, mix;
1023   struct arg_list al;
1024   char *d = "D"+!!(toys.optflags&FLAG_A), *header,
1025             deltas[] = {11,28,29,44,50,51,52,53,54};
1026   unsigned tock = 0;
1027   int i, lines;
1028
1029   if (!TT.iotop.d) TT.iotop.d = 3;
1030   if (toys.optflags&FLAG_k) TT.forcek++;
1031   if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1032   shared_main();
1033
1034   // TODO: usage: iotop [-oq]
1035
1036   comma_args(TT.iotop.u, &TT.uu, "bad -u", parse_rest);
1037   comma_args(TT.iotop.p, &TT.pp, "bad -p", parse_rest);
1038
1039   al.next = 0;
1040   al.arg = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM", d, d, d);
1041   comma_args(&al, &TT.fields, 0, parse_ko);
1042   free(al.arg);
1043   dlist_terminate(TT.fields);
1044   header = strdup(toybuf);
1045
1046   al.arg = xmprintf("-%sIO,-%sREAD,-%sWRITE,-SWAP,-ETIME,-PID",d,d,d);
1047   comma_args(&al, &TT.kfields, 0, parse_ko);
1048   free(al.arg);
1049   dlist_terminate(TT.kfields);
1050
1051   TT.ksave = DIRTREE_SAVE;
1052   TT.match_process = shared_match_process;
1053   memset(plist, 0, sizeof(plist));
1054   for (;;) {
1055     struct dirtree *dt = dirtree_read("/proc", get_ps);
1056
1057     plold = plist+(tock++&1);
1058     plnew = plist+(tock&1);
1059     plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
1060     TT.kcount = 0;
1061
1062     // First time, wait a quarter of a second to collect a little delta data.
1063     if (!plold->tb) {
1064       msleep(250);
1065       continue;
1066     }
1067
1068     // Collate old and new into "mix", depends on /proc read in pid sort order
1069     old = *plold;
1070     new = *plnew;
1071     mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1072     mix.count = 0;
1073
1074     while (old.count || new.count) {
1075       struct carveup *otb = *old.tb, *ntb = *new.tb;
1076
1077       // If we just have old, discard it.
1078       if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1079         old.tb++;
1080         old.count--;
1081
1082         continue;
1083       }
1084
1085       // If we just have new, use it verbatim
1086       if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1087       else {
1088
1089         // If we have both, adjust deltas. Stomping old data is fine because
1090         // we free it after displaying.
1091         if (!(toys.optflags&FLAG_a))
1092           for (i = 0; i<ARRAY_LEN(deltas); i++)
1093             otb->slot[deltas[i]] = ntb->slot[deltas[i]] - otb->slot[deltas[i]];
1094         if (!(toys.optflags&FLAG_o) || otb->slot[28+!(toys.optflags&FLAG_A)]) {
1095           mix.tb[mix.count] = otb;
1096           mix.count++;
1097         }
1098         old.tb++;
1099         old.count--;
1100       }
1101       new.tb++;
1102       new.count--;
1103     }
1104
1105     qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1106     if (!(toys.optflags&FLAG_q))
1107       printf("%s%s\n", (toys.optflags&FLAG_b) ? "" : "\033[H\033[J", header);
1108     lines = TT.height-2;
1109
1110     for (i=0; i<lines && i<mix.count; i++) show_ps(mix.tb[i]);
1111     free(mix.tb);
1112     for (i=0; i<plold->count; i++) free(plold->tb[i]);
1113     free(plold->tb);
1114
1115     if (TT.iotop.n) if (!--TT.iotop.n) break;
1116     msleep(1000*TT.iotop.d);
1117   }
1118 }