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