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