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