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