OSDN Git Service

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