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