10f66f451Sopenharmony_ci/* ps.c - show process list
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2015 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
60f66f451Sopenharmony_ci * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
70f66f451Sopenharmony_ci * And linux kernel source fs/proc/array.c function do_task_stat()
80f66f451Sopenharmony_ci *
90f66f451Sopenharmony_ci * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to
100f66f451Sopenharmony_ci * mean "show numeric users and groups" instead.
110f66f451Sopenharmony_ci * Posix says default output should have field named "TTY" but if you "-o tty"
120f66f451Sopenharmony_ci * the same field should be called "TT" which is _INSANE_ and I'm not doing it.
130f66f451Sopenharmony_ci * Similarly -f outputs USER but calls it UID (we call it USER).
140f66f451Sopenharmony_ci * It also says that -o "args" and "comm" should behave differently but use
150f66f451Sopenharmony_ci * the same title, which is not the same title as the default output. (No.)
160f66f451Sopenharmony_ci * Select by session id is -s not -g. Posix doesn't say truncated fields
170f66f451Sopenharmony_ci * should end with "+" but it's pretty common behavior.
180f66f451Sopenharmony_ci *
190f66f451Sopenharmony_ci * Posix defines -o ADDR as "The address of the process" but the process
200f66f451Sopenharmony_ci * start address is a constant on any elf system with mmu. The procps ADDR
210f66f451Sopenharmony_ci * field always prints "-" with an alignment of 1, which is why it has 11
220f66f451Sopenharmony_ci * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
230f66f451Sopenharmony_ci * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
240f66f451Sopenharmony_ci * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
250f66f451Sopenharmony_ci * which changes -l by removing the "F" column and swapping RSS for ADDR,
260f66f451Sopenharmony_ci * leaving 9 chars for cmd, so we're using that as our -l output.
270f66f451Sopenharmony_ci *
280f66f451Sopenharmony_ci * Added a bunch of new -o fields posix doesn't mention, and we don't
290f66f451Sopenharmony_ci * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
300f66f451Sopenharmony_ci * output argv[0] unmodified for -o comm or -o args (but procps violates
310f66f451Sopenharmony_ci * posix for -o comm anyway, it's stat[2] not argv[0]).
320f66f451Sopenharmony_ci *
330f66f451Sopenharmony_ci * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
340f66f451Sopenharmony_ci *       files (why they're not globally readable when the rest of proc
350f66f451Sopenharmony_ci *       data is...?) and get a global I/O picture. Normal top is NOT,
360f66f451Sopenharmony_ci *       even though you can -o AIO there, to give sysadmins the option
370f66f451Sopenharmony_ci *       to reduce security exposure.)
380f66f451Sopenharmony_ci *
390f66f451Sopenharmony_ci * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
400f66f451Sopenharmony_ci * TODO: switch -fl to -y
410f66f451Sopenharmony_ci * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
420f66f451Sopenharmony_ci * TODO: iotop: Window size change: respond immediately. Why not padding
430f66f451Sopenharmony_ci *       at right edge? (Not adjusting to screen size at all? Header wraps?)
440f66f451Sopenharmony_ci * TODO: top: thread support and SMP
450f66f451Sopenharmony_ci * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ciUSE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ#<1a[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE))
480f66f451Sopenharmony_ci// stayroot because iotop needs root to read other process' proc/$$/io
490f66f451Sopenharmony_ci// TOP and IOTOP have a large common option block used for common processing,
500f66f451Sopenharmony_ci// the default values are different but the flags are in the same order.
510f66f451Sopenharmony_ciUSE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d%<100=3000m#n#<1abq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
520f66f451Sopenharmony_ciUSE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d%<100=3000m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
530f66f451Sopenharmony_ciUSE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
540f66f451Sopenharmony_ciUSE_PKILL(NEWTOY(pkill,    "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ciconfig PS
570f66f451Sopenharmony_ci  bool "ps"
580f66f451Sopenharmony_ci  default y
590f66f451Sopenharmony_ci  help
600f66f451Sopenharmony_ci    usage: ps or ps -a
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ciconfig TOP
630f66f451Sopenharmony_ci  bool "top"
640f66f451Sopenharmony_ci  default y
650f66f451Sopenharmony_ci  help
660f66f451Sopenharmony_ci    usage: top or top -a
670f66f451Sopenharmony_ci
680f66f451Sopenharmony_ci# Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
690f66f451Sopenharmony_ciconfig IOTOP
700f66f451Sopenharmony_ci  bool "iotop"
710f66f451Sopenharmony_ci  default n
720f66f451Sopenharmony_ci  help
730f66f451Sopenharmony_ci    usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
740f66f451Sopenharmony_ci
750f66f451Sopenharmony_ci    Rank processes by I/O.
760f66f451Sopenharmony_ci
770f66f451Sopenharmony_ci    -A	All I/O, not just disk
780f66f451Sopenharmony_ci    -a	Accumulated I/O (not percentage)
790f66f451Sopenharmony_ci    -H	Show threads
800f66f451Sopenharmony_ci    -K	Kilobytes
810f66f451Sopenharmony_ci    -k	Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
820f66f451Sopenharmony_ci    -m	Maximum number of tasks to show
830f66f451Sopenharmony_ci    -O	Only show processes doing I/O
840f66f451Sopenharmony_ci    -o	Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
850f66f451Sopenharmony_ci    -s	Sort by field number (0-X, default 6)
860f66f451Sopenharmony_ci    -b	Batch mode (no tty)
870f66f451Sopenharmony_ci    -d	Delay SECONDS between each cycle (default 3)
880f66f451Sopenharmony_ci    -n	Exit after NUMBER iterations
890f66f451Sopenharmony_ci    -p	Show these PIDs
900f66f451Sopenharmony_ci    -u	Show these USERs
910f66f451Sopenharmony_ci    -q	Quiet (no header lines)
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci    Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
940f66f451Sopenharmony_ci    update, R to reverse sort, Q to exit.
950f66f451Sopenharmony_ci
960f66f451Sopenharmony_ciconfig PGREP
970f66f451Sopenharmony_ci  bool "pgrep"
980f66f451Sopenharmony_ci  default n
990f66f451Sopenharmony_ci  help
1000f66f451Sopenharmony_ci    usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci    Search for process(es). PATTERN is an extended regular expression checked
1030f66f451Sopenharmony_ci    against command names.
1040f66f451Sopenharmony_ci
1050f66f451Sopenharmony_ci    -c	Show only count of matches
1060f66f451Sopenharmony_ci    -d	Use DELIM instead of newline
1070f66f451Sopenharmony_ci    -L	Send SIGNAL instead of printing name
1080f66f451Sopenharmony_ci    -l	Show command name
1090f66f451Sopenharmony_ci    -f	Check full command line for PATTERN
1100f66f451Sopenharmony_ci    -G	Match real Group ID(s)
1110f66f451Sopenharmony_ci    -g	Match Process Group(s) (0 is current user)
1120f66f451Sopenharmony_ci    -n	Newest match only
1130f66f451Sopenharmony_ci    -o	Oldest match only
1140f66f451Sopenharmony_ci    -P	Match Parent Process ID(s)
1150f66f451Sopenharmony_ci    -s	Match Session ID(s) (0 for current)
1160f66f451Sopenharmony_ci    -t	Match Terminal(s)
1170f66f451Sopenharmony_ci    -U	Match real User ID(s)
1180f66f451Sopenharmony_ci    -u	Match effective User ID(s)
1190f66f451Sopenharmony_ci    -v	Negate the match
1200f66f451Sopenharmony_ci    -x	Match whole command (not substring)
1210f66f451Sopenharmony_ci
1220f66f451Sopenharmony_ciconfig PKILL
1230f66f451Sopenharmony_ci  bool "pkill"
1240f66f451Sopenharmony_ci  default n
1250f66f451Sopenharmony_ci  help
1260f66f451Sopenharmony_ci    usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
1270f66f451Sopenharmony_ci
1280f66f451Sopenharmony_ci    -l	Send SIGNAL (default SIGTERM)
1290f66f451Sopenharmony_ci    -V	Verbose
1300f66f451Sopenharmony_ci    -f	Check full command line for PATTERN
1310f66f451Sopenharmony_ci    -G	Match real Group ID(s)
1320f66f451Sopenharmony_ci    -g	Match Process Group(s) (0 is current user)
1330f66f451Sopenharmony_ci    -n	Newest match only
1340f66f451Sopenharmony_ci    -o	Oldest match only
1350f66f451Sopenharmony_ci    -P	Match Parent Process ID(s)
1360f66f451Sopenharmony_ci    -s	Match Session ID(s) (0 for current)
1370f66f451Sopenharmony_ci    -t	Match Terminal(s)
1380f66f451Sopenharmony_ci    -U	Match real User ID(s)
1390f66f451Sopenharmony_ci    -u	Match effective User ID(s)
1400f66f451Sopenharmony_ci    -v	Negate the match
1410f66f451Sopenharmony_ci    -x	Match whole command (not substring)
1420f66f451Sopenharmony_ci*/
1430f66f451Sopenharmony_ci
1440f66f451Sopenharmony_ci#define FOR_ps
1450f66f451Sopenharmony_ci#include "toys.h"
1460f66f451Sopenharmony_ci
1470f66f451Sopenharmony_ciGLOBALS(
1480f66f451Sopenharmony_ci  union {
1490f66f451Sopenharmony_ci    struct {
1500f66f451Sopenharmony_ci      struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k;
1510f66f451Sopenharmony_ci    } ps;
1520f66f451Sopenharmony_ci    struct {
1530f66f451Sopenharmony_ci      long n, m, d, s;
1540f66f451Sopenharmony_ci      struct arg_list *u, *p, *o, *k, *O;
1550f66f451Sopenharmony_ci    } top;
1560f66f451Sopenharmony_ci    struct {
1570f66f451Sopenharmony_ci      char *L;
1580f66f451Sopenharmony_ci      struct arg_list *G, *g, *P, *s, *t, *U, *u;
1590f66f451Sopenharmony_ci      char *d;
1600f66f451Sopenharmony_ci
1610f66f451Sopenharmony_ci      void *regexes, *snapshot;
1620f66f451Sopenharmony_ci      int signal;
1630f66f451Sopenharmony_ci      pid_t self, match;
1640f66f451Sopenharmony_ci    } pgrep;
1650f66f451Sopenharmony_ci  };
1660f66f451Sopenharmony_ci
1670f66f451Sopenharmony_ci  struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
1680f66f451Sopenharmony_ci  struct dirtree *threadparent;
1690f66f451Sopenharmony_ci  unsigned width, height;
1700f66f451Sopenharmony_ci  dev_t tty;
1710f66f451Sopenharmony_ci  void *fields, *kfields;
1720f66f451Sopenharmony_ci  long long ticks, bits, time;
1730f66f451Sopenharmony_ci  int kcount, forcek, sortpos;
1740f66f451Sopenharmony_ci  int (*match_process)(long long *slot);
1750f66f451Sopenharmony_ci  void (*show_process)(void *tb);
1760f66f451Sopenharmony_ci)
1770f66f451Sopenharmony_ci
1780f66f451Sopenharmony_ci// Linked list of -o fields selected for display, in order, with :len and =title
1790f66f451Sopenharmony_ci
1800f66f451Sopenharmony_cistruct ofields {
1810f66f451Sopenharmony_ci  struct ofields *next, *prev;
1820f66f451Sopenharmony_ci  short which, len, reverse;
1830f66f451Sopenharmony_ci  char *title;
1840f66f451Sopenharmony_ci};
1850f66f451Sopenharmony_ci
1860f66f451Sopenharmony_ci/* The function get_ps() reads all the data about one process, saving it in
1870f66f451Sopenharmony_ci * toybox as a struct procpid. Simple ps calls then pass toybuf directly to
1880f66f451Sopenharmony_ci * show_ps(), but features like sorting append a copy to a linked list
1890f66f451Sopenharmony_ci * for further processing once all processes have been read.
1900f66f451Sopenharmony_ci *
1910f66f451Sopenharmony_ci * struct procpid contains a slot[] array of 64 bit values, with the following
1920f66f451Sopenharmony_ci * data at each position in the array. Most is read from /proc/$PID/stat (see
1930f66f451Sopenharmony_ci * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but
1940f66f451Sopenharmony_ci * we replace several fields with don't use with other data. */
1950f66f451Sopenharmony_ci
1960f66f451Sopenharmony_cienum {
1970f66f451Sopenharmony_ci SLOT_pid,      /*process id*/            SLOT_ppid,      // parent process id
1980f66f451Sopenharmony_ci SLOT_pgrp,     /*process group*/         SLOT_sid,       // session id
1990f66f451Sopenharmony_ci SLOT_ttynr,    /*tty the process uses*/  SLOT_ttypgrp,   // pgrp of the tty
2000f66f451Sopenharmony_ci SLOT_flags,    /*task flags*/            SLOT_minflt,    // minor faults
2010f66f451Sopenharmony_ci SLOT_cminflt,  /*minor faults+child*/    SLOT_majflt,    // major faults
2020f66f451Sopenharmony_ci SLOT_cmajflt,  /*major faults+child*/    SLOT_utime,     // user+kernel jiffies
2030f66f451Sopenharmony_ci SLOT_stime,    /*kernel mode jiffies*/   SLOT_cutime,    // utime+child utime
2040f66f451Sopenharmony_ci SLOT_cstime,   /*stime+child*/           SLOT_priority,  // priority level
2050f66f451Sopenharmony_ci SLOT_nice,     /*nice level*/            SLOT_numthreads,// thread count
2060f66f451Sopenharmony_ci SLOT_vmlck,    /*locked memory*/         SLOT_starttime, // jiffies after boot
2070f66f451Sopenharmony_ci SLOT_vsize,    /*virtual memory size*/   SLOT_rss,       // resident set size
2080f66f451Sopenharmony_ci SLOT_rsslim,   /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
2090f66f451Sopenharmony_ci SLOT_endcode,  /*code segment address*/  SLOT_startstack,// stack address
2100f66f451Sopenharmony_ci SLOT_esp,      /*task stack pointer*/    SLOT_eip,       // instruction pointer
2110f66f451Sopenharmony_ci SLOT_iobytes,  /*All I/O bytes*/         SLOT_diobytes,  // disk I/O bytes
2120f66f451Sopenharmony_ci SLOT_utime2,   /*relative utime (top)*/  SLOT_uid,       // user id
2130f66f451Sopenharmony_ci SLOT_ruid,     /*real user id*/          SLOT_gid,       // group id
2140f66f451Sopenharmony_ci SLOT_rgid,     /*real group id*/         SLOT_exitsig,   // sent to parent
2150f66f451Sopenharmony_ci SLOT_taskcpu,  /*CPU running on*/        SLOT_rtprio,    // realtime priority
2160f66f451Sopenharmony_ci SLOT_policy,   /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
2170f66f451Sopenharmony_ci SLOT_gtime,    /*guest jiffies of task*/ SLOT_cgtime,    // gtime+child
2180f66f451Sopenharmony_ci SLOT_startbss, /*data/bss address*/      SLOT_endbss,    // end addr data+bss
2190f66f451Sopenharmony_ci// end of /proc/$PID/stat fields
2200f66f451Sopenharmony_ci SLOT_upticks,  /*uptime-starttime*/      SLOT_argv0len,  // argv[0] length
2210f66f451Sopenharmony_ci SLOT_uptime,   /*sysinfo.uptime*/        SLOT_totalram,  // sysinfo.totalram
2220f66f451Sopenharmony_ci SLOT_vsz,      /*Virtual mem Size*/      SLOT_shr,       // Shared memory
2230f66f451Sopenharmony_ci SLOT_pcy,      /*Android sched pol*/     SLOT_rchar,     // All bytes read
2240f66f451Sopenharmony_ci SLOT_wchar,    /*All bytes written*/     SLOT_rbytes,    // Disk bytes read
2250f66f451Sopenharmony_ci SLOT_wbytes,   /*Disk bytes written*/    SLOT_swap,      // Swap pages used
2260f66f451Sopenharmony_ci SLOT_bits,     /*32 or 64*/              SLOT_tid,       // Thread ID
2270f66f451Sopenharmony_ci SLOT_tcount,   /*Thread count*/
2280f66f451Sopenharmony_ci
2290f66f451Sopenharmony_ci SLOT_count /* Size of array */
2300f66f451Sopenharmony_ci};
2310f66f451Sopenharmony_ci
2320f66f451Sopenharmony_ci/* In addition to slot[], carevup contains 6 string fields to display
2330f66f451Sopenharmony_ci   command name, tty device, selinux label... They're stored one after the
2340f66f451Sopenharmony_ci   other in str[] (separated by null terminators), and offset[] contains the
2350f66f451Sopenharmony_ci   starting position of each string after the first (which is always 0). */
2360f66f451Sopenharmony_ci
2370f66f451Sopenharmony_ci// Data layout in toybuf
2380f66f451Sopenharmony_cistruct procpid {
2390f66f451Sopenharmony_ci  long long slot[SLOT_count]; // data (see enum above)
2400f66f451Sopenharmony_ci  unsigned short offset[6];   // offset of fields in str[] (skip CMD, always 0)
2410f66f451Sopenharmony_ci  char state;
2420f66f451Sopenharmony_ci  char str[];                 // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME
2430f66f451Sopenharmony_ci};
2440f66f451Sopenharmony_ci
2450f66f451Sopenharmony_ci/* The typos[] array lists all the types understood by "ps -o", I.E all the
2460f66f451Sopenharmony_ci * columns ps and top know how to display. Each entry has:
2470f66f451Sopenharmony_ci *
2480f66f451Sopenharmony_ci * name: the column name, displayed at top and used to select column with -o
2490f66f451Sopenharmony_ci *
2500f66f451Sopenharmony_ci * width: the display width. Fields are padded to this width when displaying
2510f66f451Sopenharmony_ci *        to a terminal (negative means right justified). Strings are truncated
2520f66f451Sopenharmony_ci *        to fit, numerical fields are padded but not truncated (although
2530f66f451Sopenharmony_ci *        the display code reclaims unused padding from later fields to try to
2540f66f451Sopenharmony_ci *        get the overflow back).
2550f66f451Sopenharmony_ci *
2560f66f451Sopenharmony_ci * slot: which slot[] out of procpid. Negative means it's a string field.
2570f66f451Sopenharmony_ci *       value|XX requests extra display/sort processing.
2580f66f451Sopenharmony_ci *
2590f66f451Sopenharmony_ci * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the
2600f66f451Sopenharmony_ci * first string argument and the prefix is the first argument to TAGGED_ARRAY
2610f66f451Sopenharmony_ci * so in this case "NAME" becomes PS_NAME which is the offset into typos[]
2620f66f451Sopenharmony_ci * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME).
2630f66f451Sopenharmony_ci * We record active columns in TT.bits, ala:
2640f66f451Sopenharmony_ci *
2650f66f451Sopenharmony_ci *   if (TT.bits & _PS_NAME) printf("-o included PS_NAME");
2660f66f451Sopenharmony_ci */
2670f66f451Sopenharmony_ci
2680f66f451Sopenharmony_ci#define XX 64 // force string representation for sorting, etc
2690f66f451Sopenharmony_ci
2700f66f451Sopenharmony_ci// TODO: Android uses -30 for LABEL, but ideally it would auto-size.
2710f66f451Sopenharmony_cistruct typography {
2720f66f451Sopenharmony_ci  char *name, *help;
2730f66f451Sopenharmony_ci  signed char width, slot;
2740f66f451Sopenharmony_ci} static const typos[] = TAGGED_ARRAY(PS,
2750f66f451Sopenharmony_ci  // Numbers. (What's in slot[] is what's displayed, sorted numerically.)
2760f66f451Sopenharmony_ci  {"PID", "Process ID", 5, SLOT_pid},
2770f66f451Sopenharmony_ci  {"PPID", "Parent Process ID", 5, SLOT_ppid},
2780f66f451Sopenharmony_ci  {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority},
2790f66f451Sopenharmony_ci  {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice},
2800f66f451Sopenharmony_ci  {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip},
2810f66f451Sopenharmony_ci  {"SZ", "4k pages to swap out", 5, SLOT_vsize},
2820f66f451Sopenharmony_ci  {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss},
2830f66f451Sopenharmony_ci  {"PGID", "Process Group ID", 5, SLOT_pgrp},
2840f66f451Sopenharmony_ci  {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize},
2850f66f451Sopenharmony_ci  {"MAJFL", "Major page faults", 6, SLOT_majflt},
2860f66f451Sopenharmony_ci  {"MINFL", "Minor page faults", 6, SLOT_minflt},
2870f66f451Sopenharmony_ci  {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority},
2880f66f451Sopenharmony_ci  {"PSR", "Processor last executed on", 3, SLOT_taskcpu},
2890f66f451Sopenharmony_ci  {"RTPRIO", "Realtime priority", 6, SLOT_rtprio},
2900f66f451Sopenharmony_ci  {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)",
2910f66f451Sopenharmony_ci   3, SLOT_policy},
2920f66f451Sopenharmony_ci  {"CPU", "Which processor running on", 3, SLOT_taskcpu},
2930f66f451Sopenharmony_ci  {"TID", "Thread ID", 5, SLOT_tid},
2940f66f451Sopenharmony_ci  {"TCNT", "Thread count", 4, SLOT_tcount},
2950f66f451Sopenharmony_ci  {"BIT", "32 or 64", 3, SLOT_bits},
2960f66f451Sopenharmony_ci
2970f66f451Sopenharmony_ci  // String fields (-1 is procpid->str, rest are str+offset[1-slot])
2980f66f451Sopenharmony_ci  {"TTY", "Controlling terminal", -8, -2},
2990f66f451Sopenharmony_ci  {"WCHAN", "Wait location in kernel", -6, -3},
3000f66f451Sopenharmony_ci  {"LABEL", "Security label", -30, -4},
3010f66f451Sopenharmony_ci  {"COMM", "EXE filename (/proc/PID/exe)", -27, -5},
3020f66f451Sopenharmony_ci  {"NAME", "Process name (PID's argv[0])", -27, -7},
3030f66f451Sopenharmony_ci  {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5},
3040f66f451Sopenharmony_ci  {"CMDLINE", "Command line (argv[])", -27, -6},
3050f66f451Sopenharmony_ci  {"ARGS", "CMDLINE minus initial path", -27, -6},
3060f66f451Sopenharmony_ci  {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1},
3070f66f451Sopenharmony_ci
3080f66f451Sopenharmony_ci  // user/group (may call getpwuid() or similar)
3090f66f451Sopenharmony_ci  {"UID", "User id", 5, SLOT_uid},
3100f66f451Sopenharmony_ci  {"USER", "User name", -12, XX|SLOT_uid},
3110f66f451Sopenharmony_ci  {"RUID", "Real (before suid) user ID", 4, SLOT_ruid},
3120f66f451Sopenharmony_ci  {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid},
3130f66f451Sopenharmony_ci  {"GID", "Group ID", 8, SLOT_gid},
3140f66f451Sopenharmony_ci  {"GROUP", "Group name", -8, XX|SLOT_gid},
3150f66f451Sopenharmony_ci  {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid},
3160f66f451Sopenharmony_ci  {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid},
3170f66f451Sopenharmony_ci
3180f66f451Sopenharmony_ci  // clock displays (00:00:00)
3190f66f451Sopenharmony_ci  {"TIME", "CPU time consumed", 8, SLOT_utime},
3200f66f451Sopenharmony_ci  {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime},
3210f66f451Sopenharmony_ci  {"TIME+", "CPU time (high precision)", 9, SLOT_utime},
3220f66f451Sopenharmony_ci
3230f66f451Sopenharmony_ci  // Percentage displays (fixed point, one decimal digit. 123 -> 12.3)
3240f66f451Sopenharmony_ci  {"C", "Total %CPU used since start", 1, SLOT_utime2},
3250f66f451Sopenharmony_ci  {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize},
3260f66f451Sopenharmony_ci  {"%MEM", "RSS as % of physical memory", 5, SLOT_rss},
3270f66f451Sopenharmony_ci  {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2},
3280f66f451Sopenharmony_ci
3290f66f451Sopenharmony_ci  // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc)
3300f66f451Sopenharmony_ci  {"VIRT", "Virtual memory size", 4, SLOT_vsz},
3310f66f451Sopenharmony_ci  {"RES", "Short RSS", 4, SLOT_rss},
3320f66f451Sopenharmony_ci  {"SHR", "Shared memory", 4, SLOT_shr},
3330f66f451Sopenharmony_ci  {"READ", "Data read", 6, SLOT_rchar},
3340f66f451Sopenharmony_ci  {"WRITE", "Data written", 6, SLOT_wchar},
3350f66f451Sopenharmony_ci  {"IO", "Data I/O", 6, SLOT_iobytes},
3360f66f451Sopenharmony_ci  {"DREAD", "Data read from disk", 6, SLOT_rbytes},
3370f66f451Sopenharmony_ci  {"DWRITE", "Data written to disk", 6, SLOT_wbytes},
3380f66f451Sopenharmony_ci  {"SWAP", "Swap I/O", 6, SLOT_swap},
3390f66f451Sopenharmony_ci  {"DIO", "Disk I/O", 6, SLOT_diobytes},
3400f66f451Sopenharmony_ci
3410f66f451Sopenharmony_ci  // Misc (special cases)
3420f66f451Sopenharmony_ci  {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime},
3430f66f451Sopenharmony_ci  {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags},
3440f66f451Sopenharmony_ci  {"S", "Process state:\n"
3450f66f451Sopenharmony_ci   "\t  R (running) S (sleeping) D (device I/O) T (stopped)  t (trace stop)\n"
3460f66f451Sopenharmony_ci   "\t  X (dead)    Z (zombie)   P (parked)     I (idle)\n"
3470f66f451Sopenharmony_ci   "\t  Also between Linux 2.6.33 and 3.13:\n"
3480f66f451Sopenharmony_ci   "\t  x (dead)    K (wakekill) W (waking)\n",
3490f66f451Sopenharmony_ci   -1, XX},
3500f66f451Sopenharmony_ci  {"STAT", "Process state (S) plus:\n"
3510f66f451Sopenharmony_ci   "\t  < high priority          N low priority L locked memory\n"
3520f66f451Sopenharmony_ci   "\t  s session leader         + foreground   l multithreaded",
3530f66f451Sopenharmony_ci   -5, XX},
3540f66f451Sopenharmony_ci  {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy},
3550f66f451Sopenharmony_ci);
3560f66f451Sopenharmony_ci
3570f66f451Sopenharmony_ci// Show sorted "-o help" text for fields listed in toybuf[len]
3580f66f451Sopenharmony_cistatic void help_fields(int len, int multi)
3590f66f451Sopenharmony_ci{
3600f66f451Sopenharmony_ci  int i, j, k, left = 0;
3610f66f451Sopenharmony_ci  struct typography *t;
3620f66f451Sopenharmony_ci
3630f66f451Sopenharmony_ci  // Quick and dirty sort of toybuf[] entries (see TODO below)
3640f66f451Sopenharmony_ci  for (j = len; j--; ) {
3650f66f451Sopenharmony_ci    k = -1;
3660f66f451Sopenharmony_ci
3670f66f451Sopenharmony_ci    for (i=0; i<j; i++) {
3680f66f451Sopenharmony_ci      if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) {
3690f66f451Sopenharmony_ci        k = toybuf[i];
3700f66f451Sopenharmony_ci        toybuf[i] = toybuf[i+1];
3710f66f451Sopenharmony_ci        toybuf[i+1] = k;
3720f66f451Sopenharmony_ci      }
3730f66f451Sopenharmony_ci    }
3740f66f451Sopenharmony_ci    if (k == -1) break;
3750f66f451Sopenharmony_ci  }
3760f66f451Sopenharmony_ci
3770f66f451Sopenharmony_ci  // Display loop
3780f66f451Sopenharmony_ci  for (i = j = 0; i<len; i++, j++) {
3790f66f451Sopenharmony_ci    t = (void *)(typos+toybuf[i]);
3800f66f451Sopenharmony_ci    if (strlen(t->help)>30) {
3810f66f451Sopenharmony_ci      if (multi) printf("  %-8s%s\n", t->name, t->help);
3820f66f451Sopenharmony_ci      else j--;
3830f66f451Sopenharmony_ci    } else if (!multi) {
3840f66f451Sopenharmony_ci      left = !(j&1);
3850f66f451Sopenharmony_ci      printf("  %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left);
3860f66f451Sopenharmony_ci    }
3870f66f451Sopenharmony_ci  }
3880f66f451Sopenharmony_ci  if (!multi && left) xputc('\n');
3890f66f451Sopenharmony_ci}
3900f66f451Sopenharmony_ci
3910f66f451Sopenharmony_ci// Print help text for each -o field, with categories.
3920f66f451Sopenharmony_cistatic void help_help(void)
3930f66f451Sopenharmony_ci{
3940f66f451Sopenharmony_ci  int i, jump = PS_CMD+1-PS_COMM;
3950f66f451Sopenharmony_ci
3960f66f451Sopenharmony_ci  // TODO: sort the array of -o types so they're already alphabetical and
3970f66f451Sopenharmony_ci  // don't need sorting here. A regex to find everything that currently cares
3980f66f451Sopenharmony_ci  // about symbol order might be: "which *[><]=* *PS"
3990f66f451Sopenharmony_ci
4000f66f451Sopenharmony_ci  // First show the half-dozen variants of command line display.
4010f66f451Sopenharmony_ci
4020f66f451Sopenharmony_ci  printf("Command line field types:\n\n");
4030f66f451Sopenharmony_ci  for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i;
4040f66f451Sopenharmony_ci  help_fields(jump, 0);
4050f66f451Sopenharmony_ci
4060f66f451Sopenharmony_ci  // Show the rest of the -o types, starting with the ones that don't columnize
4070f66f451Sopenharmony_ci
4080f66f451Sopenharmony_ci  printf("\nProcess attribute field types:\n\n");
4090f66f451Sopenharmony_ci  for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump;
4100f66f451Sopenharmony_ci  help_fields(ARRAY_LEN(typos)-jump, 1);
4110f66f451Sopenharmony_ci  help_fields(ARRAY_LEN(typos)-jump, 0);
4120f66f451Sopenharmony_ci
4130f66f451Sopenharmony_ci  xexit();
4140f66f451Sopenharmony_ci}
4150f66f451Sopenharmony_ci
4160f66f451Sopenharmony_ci// process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep
4170f66f451Sopenharmony_cistatic int shared_match_process(long long *slot)
4180f66f451Sopenharmony_ci{
4190f66f451Sopenharmony_ci  struct ptr_len match[] = {
4200f66f451Sopenharmony_ci    {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
4210f66f451Sopenharmony_ci    {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
4220f66f451Sopenharmony_ci    {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
4230f66f451Sopenharmony_ci  };
4240f66f451Sopenharmony_ci  int i, j;
4250f66f451Sopenharmony_ci  long *ll = 0;
4260f66f451Sopenharmony_ci
4270f66f451Sopenharmony_ci  // Do we have -g -G -p -P -s -t -u -U options selecting processes?
4280f66f451Sopenharmony_ci  for (i = 0; i < ARRAY_LEN(match); i++) {
4290f66f451Sopenharmony_ci    struct ptr_len *mm = match[i].ptr;
4300f66f451Sopenharmony_ci
4310f66f451Sopenharmony_ci    if (mm->len) {
4320f66f451Sopenharmony_ci      ll = mm->ptr;
4330f66f451Sopenharmony_ci      for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
4340f66f451Sopenharmony_ci    }
4350f66f451Sopenharmony_ci  }
4360f66f451Sopenharmony_ci
4370f66f451Sopenharmony_ci  return ll ? 0 : -1;
4380f66f451Sopenharmony_ci}
4390f66f451Sopenharmony_ci
4400f66f451Sopenharmony_ci// process match filter for ps: Return 0 to discard, nonzero to keep
4410f66f451Sopenharmony_cistatic int ps_match_process(long long *slot)
4420f66f451Sopenharmony_ci{
4430f66f451Sopenharmony_ci  int i = shared_match_process(slot);
4440f66f451Sopenharmony_ci
4450f66f451Sopenharmony_ci  if (i>0) return 1;
4460f66f451Sopenharmony_ci  // If we had selections and didn't match them, don't display
4470f66f451Sopenharmony_ci  if (!i) return 0;
4480f66f451Sopenharmony_ci
4490f66f451Sopenharmony_ci  // Filter implicit categories for other display types
4500f66f451Sopenharmony_ci  if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0;
4510f66f451Sopenharmony_ci  if (FLAG(a) && !slot[SLOT_ttynr]) return 0;
4520f66f451Sopenharmony_ci  if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr])
4530f66f451Sopenharmony_ci    return 0;
4540f66f451Sopenharmony_ci
4550f66f451Sopenharmony_ci  return 1;
4560f66f451Sopenharmony_ci}
4570f66f451Sopenharmony_ci
4580f66f451Sopenharmony_ci// Generate display string (260 bytes at end of toybuf) from struct ofield
4590f66f451Sopenharmony_cistatic char *string_field(struct procpid *tb, struct ofields *field)
4600f66f451Sopenharmony_ci{
4610f66f451Sopenharmony_ci  char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
4620f66f451Sopenharmony_ci  int which = field->which, sl = typos[which].slot;
4630f66f451Sopenharmony_ci  long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0;
4640f66f451Sopenharmony_ci
4650f66f451Sopenharmony_ci  // numbers, mostly from /proc/$PID/stat
4660f66f451Sopenharmony_ci  if (which <= PS_BIT) {
4670f66f451Sopenharmony_ci    char *fmt = "%lld";
4680f66f451Sopenharmony_ci
4690f66f451Sopenharmony_ci    if (which==PS_PRI) ll = 39-ll;
4700f66f451Sopenharmony_ci    if (which==PS_ADDR) fmt = "%llx";
4710f66f451Sopenharmony_ci    else if (which==PS_SZ) ll >>= 12;
4720f66f451Sopenharmony_ci    else if (which==PS_RSS) ll <<= 2;
4730f66f451Sopenharmony_ci    else if (which==PS_VSZ) ll >>= 10;
4740f66f451Sopenharmony_ci    else if (which==PS_PR && ll<-9) fmt="RT";
4750f66f451Sopenharmony_ci    else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
4760f66f451Sopenharmony_ci    sprintf(out, fmt, ll);
4770f66f451Sopenharmony_ci
4780f66f451Sopenharmony_ci  // String fields
4790f66f451Sopenharmony_ci  } else if (sl < 0) {
4800f66f451Sopenharmony_ci    out = tb->str;
4810f66f451Sopenharmony_ci    sl *= -1;
4820f66f451Sopenharmony_ci    // First string slot has offset 0, others are offset[-slot-2]
4830f66f451Sopenharmony_ci    if (--sl) out += tb->offset[--sl];
4840f66f451Sopenharmony_ci    if (which==PS_ARGS || which==PS_COMM) {
4850f66f451Sopenharmony_ci      int i;
4860f66f451Sopenharmony_ci
4870f66f451Sopenharmony_ci      s = out;
4880f66f451Sopenharmony_ci      for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
4890f66f451Sopenharmony_ci        if (out[i] == '/') s = out+i+1;
4900f66f451Sopenharmony_ci      out = s;
4910f66f451Sopenharmony_ci    }
4920f66f451Sopenharmony_ci    if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
4930f66f451Sopenharmony_ci
4940f66f451Sopenharmony_ci  // user/group
4950f66f451Sopenharmony_ci  } else if (which <= PS_RGROUP) {
4960f66f451Sopenharmony_ci    sprintf(out, "%lld", ll);
4970f66f451Sopenharmony_ci    if (sl&XX) {
4980f66f451Sopenharmony_ci      if (which > PS_RUSER) {
4990f66f451Sopenharmony_ci        struct group *gr = bufgetgrgid(ll);
5000f66f451Sopenharmony_ci
5010f66f451Sopenharmony_ci        if (gr) out = gr->gr_name;
5020f66f451Sopenharmony_ci      } else {
5030f66f451Sopenharmony_ci        struct passwd *pw = bufgetpwuid(ll);
5040f66f451Sopenharmony_ci
5050f66f451Sopenharmony_ci        if (pw) out = pw->pw_name;
5060f66f451Sopenharmony_ci      }
5070f66f451Sopenharmony_ci    }
5080f66f451Sopenharmony_ci
5090f66f451Sopenharmony_ci  // Clock displays
5100f66f451Sopenharmony_ci  } else if (which <= PS_TIME_) {
5110f66f451Sopenharmony_ci    int unit = 60, pad = 2, j = TT.ticks;
5120f66f451Sopenharmony_ci    time_t seconds;
5130f66f451Sopenharmony_ci
5140f66f451Sopenharmony_ci    if (which!=PS_TIME_) unit *= 60*24;
5150f66f451Sopenharmony_ci    else pad = 0;
5160f66f451Sopenharmony_ci    // top adjusts slot[SLOT_upticks], we want original meaning.
5170f66f451Sopenharmony_ci    if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
5180f66f451Sopenharmony_ci    seconds = ll/j;
5190f66f451Sopenharmony_ci
5200f66f451Sopenharmony_ci    // Output days-hours:mins:secs, skipping non-required fields with zero
5210f66f451Sopenharmony_ci    // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
5220f66f451Sopenharmony_ci    for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
5230f66f451Sopenharmony_ci      if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
5240f66f451Sopenharmony_ci      if (s) {
5250f66f451Sopenharmony_ci        s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
5260f66f451Sopenharmony_ci        pad = 2;
5270f66f451Sopenharmony_ci        if ((*s = "-::"[j])) s++;
5280f66f451Sopenharmony_ci      }
5290f66f451Sopenharmony_ci      seconds %= unit;
5300f66f451Sopenharmony_ci      unit /= j ? 60 : 24;
5310f66f451Sopenharmony_ci    }
5320f66f451Sopenharmony_ci    if (which==PS_TIME_ && s-out<8)
5330f66f451Sopenharmony_ci      sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
5340f66f451Sopenharmony_ci
5350f66f451Sopenharmony_ci  // Percentage displays
5360f66f451Sopenharmony_ci  } else if (which <= PS__CPU) {
5370f66f451Sopenharmony_ci    ll = slot[sl&(XX-1)]*1000;
5380f66f451Sopenharmony_ci    if (which==PS__VSZ || which==PS__MEM)
5390f66f451Sopenharmony_ci      ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096);
5400f66f451Sopenharmony_ci    else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
5410f66f451Sopenharmony_ci    sl = ll;
5420f66f451Sopenharmony_ci    if (which==PS_C) sl += 5;
5430f66f451Sopenharmony_ci    sprintf(out, "%d", sl/10);
5440f66f451Sopenharmony_ci    if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
5450f66f451Sopenharmony_ci
5460f66f451Sopenharmony_ci  // Human readable
5470f66f451Sopenharmony_ci  } else if (which <= PS_DIO) {
5480f66f451Sopenharmony_ci    int i = abs(field->len);
5490f66f451Sopenharmony_ci
5500f66f451Sopenharmony_ci    if (i<4) i = 4;
5510f66f451Sopenharmony_ci    s = out;
5520f66f451Sopenharmony_ci    if ((ll = slot[typos[which].slot])<0) {
5530f66f451Sopenharmony_ci      ll = -ll;
5540f66f451Sopenharmony_ci      *s++ = '-';
5550f66f451Sopenharmony_ci      if (i>4) i--;
5560f66f451Sopenharmony_ci    }
5570f66f451Sopenharmony_ci    if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
5580f66f451Sopenharmony_ci    if (TT.forcek) sprintf(out, "%lldk", ll/1024);
5590f66f451Sopenharmony_ci    else human_readable_long(s, ll, i-1, 0);
5600f66f451Sopenharmony_ci
5610f66f451Sopenharmony_ci  // Posix doesn't specify what flags should say. Man page says
5620f66f451Sopenharmony_ci  // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
5630f66f451Sopenharmony_ci  } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
5640f66f451Sopenharmony_ci  else if (which==PS_S || which==PS_STAT) {
5650f66f451Sopenharmony_ci    s = out;
5660f66f451Sopenharmony_ci    *s++ = tb->state;
5670f66f451Sopenharmony_ci    if (which==PS_STAT) {
5680f66f451Sopenharmony_ci      // TODO l = multithreaded
5690f66f451Sopenharmony_ci      if (slot[SLOT_nice]<0) *s++ = '<';
5700f66f451Sopenharmony_ci      else if (slot[SLOT_nice]>0) *s++ = 'N';
5710f66f451Sopenharmony_ci      if (slot[SLOT_sid]==*slot) *s++ = 's';
5720f66f451Sopenharmony_ci      if (slot[SLOT_vmlck]) *s++ = 'L';
5730f66f451Sopenharmony_ci      if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
5740f66f451Sopenharmony_ci    }
5750f66f451Sopenharmony_ci    *s = 0;
5760f66f451Sopenharmony_ci  } else if (which==PS_STIME) {
5770f66f451Sopenharmony_ci    time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
5780f66f451Sopenharmony_ci
5790f66f451Sopenharmony_ci    // Padding behavior's a bit odd: default field size is just hh:mm.
5800f66f451Sopenharmony_ci    // Increasing stime:size reveals more data at left until full,
5810f66f451Sopenharmony_ci    // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
5820f66f451Sopenharmony_ci    // then add :ss on right for :19.
5830f66f451Sopenharmony_ci    strftime(out, 260, "%F %T", localtime(&t));
5840f66f451Sopenharmony_ci    out = out+strlen(out)-3-abs(field->len);
5850f66f451Sopenharmony_ci    if (out<buf) out = buf;
5860f66f451Sopenharmony_ci
5870f66f451Sopenharmony_ci  } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
5880f66f451Sopenharmony_ci  else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
5890f66f451Sopenharmony_ci
5900f66f451Sopenharmony_ci  return out;
5910f66f451Sopenharmony_ci}
5920f66f451Sopenharmony_ci
5930f66f451Sopenharmony_ci// Display process data that get_ps() read from /proc, formatting via TT.fields
5940f66f451Sopenharmony_cistatic void show_ps(void *p)
5950f66f451Sopenharmony_ci{
5960f66f451Sopenharmony_ci  struct procpid *tb = p;
5970f66f451Sopenharmony_ci  struct ofields *field;
5980f66f451Sopenharmony_ci  int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
5990f66f451Sopenharmony_ci
6000f66f451Sopenharmony_ci  // Loop through fields to display
6010f66f451Sopenharmony_ci  for (field = TT.fields; field; field = field->next) {
6020f66f451Sopenharmony_ci    char *out = string_field(tb, field);
6030f66f451Sopenharmony_ci
6040f66f451Sopenharmony_ci    // Output the field, appropriately padded
6050f66f451Sopenharmony_ci
6060f66f451Sopenharmony_ci    // Minimum one space between each field
6070f66f451Sopenharmony_ci    if (width<2) break;
6080f66f451Sopenharmony_ci    if (field != TT.fields) {
6090f66f451Sopenharmony_ci      putchar(' ');
6100f66f451Sopenharmony_ci      width--;
6110f66f451Sopenharmony_ci    }
6120f66f451Sopenharmony_ci
6130f66f451Sopenharmony_ci    // Don't truncate number fields, but try to reclaim extra offset from later
6140f66f451Sopenharmony_ci    // fields that can naturally be shorter
6150f66f451Sopenharmony_ci    abslen = abs(field->len);
6160f66f451Sopenharmony_ci    sign = field->len<0 ? -1 : 1;
6170f66f451Sopenharmony_ci    olen = (TT.tty) ? utf8len(out) : strlen(out);
6180f66f451Sopenharmony_ci    if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) {
6190f66f451Sopenharmony_ci      // overflow but remember by how much
6200f66f451Sopenharmony_ci      extra += olen-abslen;
6210f66f451Sopenharmony_ci      abslen = olen;
6220f66f451Sopenharmony_ci    } else if (extra && olen<abslen) {
6230f66f451Sopenharmony_ci      int unused = abslen-olen;
6240f66f451Sopenharmony_ci
6250f66f451Sopenharmony_ci      // If later fields have slack space, take back overflow
6260f66f451Sopenharmony_ci      if (unused>extra) unused = extra;
6270f66f451Sopenharmony_ci      abslen -= unused;
6280f66f451Sopenharmony_ci      extra -= unused;
6290f66f451Sopenharmony_ci    }
6300f66f451Sopenharmony_ci    if (abslen>width) abslen = width;
6310f66f451Sopenharmony_ci    len = pad = abslen;
6320f66f451Sopenharmony_ci    pad *= sign;
6330f66f451Sopenharmony_ci
6340f66f451Sopenharmony_ci    // If last field is left justified, no trailing spaces.
6350f66f451Sopenharmony_ci    if (!field->next && sign<0) {
6360f66f451Sopenharmony_ci      pad = -1;
6370f66f451Sopenharmony_ci      len = width;
6380f66f451Sopenharmony_ci    }
6390f66f451Sopenharmony_ci
6400f66f451Sopenharmony_ci    // If we truncated a left-justified field, show + instead of last char
6410f66f451Sopenharmony_ci    if (olen>len && len>1 && sign<0) {
6420f66f451Sopenharmony_ci      width--;
6430f66f451Sopenharmony_ci      len--;
6440f66f451Sopenharmony_ci      if (field->next) pad++;
6450f66f451Sopenharmony_ci      abslen = 0;
6460f66f451Sopenharmony_ci    }
6470f66f451Sopenharmony_ci
6480f66f451Sopenharmony_ci    if (TT.tty) width -= draw_trim(out, pad, len);
6490f66f451Sopenharmony_ci    else width -= printf("%*.*s", pad, len, out);
6500f66f451Sopenharmony_ci    if (!abslen) putchar('+');
6510f66f451Sopenharmony_ci    if (!width) break;
6520f66f451Sopenharmony_ci  }
6530f66f451Sopenharmony_ci  putchar(TT.time ? '\r' : '\n');
6540f66f451Sopenharmony_ci}
6550f66f451Sopenharmony_ci
6560f66f451Sopenharmony_ci// dirtree callback: read data about a process, then display or store it.
6570f66f451Sopenharmony_ci// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra
6580f66f451Sopenharmony_ci// (in -k mode) or calls show_ps directly on toybuf (for low memory systems).
6590f66f451Sopenharmony_cistatic int get_ps(struct dirtree *new)
6600f66f451Sopenharmony_ci{
6610f66f451Sopenharmony_ci  struct {
6620f66f451Sopenharmony_ci    char *name;     // Path under /proc/$PID directory
6630f66f451Sopenharmony_ci    long long bits; // Only fetch extra data if an -o field is displaying it
6640f66f451Sopenharmony_ci  } fetch[] = {
6650f66f451Sopenharmony_ci    // sources for procpid->offset[] data
6660f66f451Sopenharmony_ci    {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
6670f66f451Sopenharmony_ci    {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
6680f66f451Sopenharmony_ci    {"", _PS_NAME}
6690f66f451Sopenharmony_ci  };
6700f66f451Sopenharmony_ci  struct procpid *tb = (void *)toybuf;
6710f66f451Sopenharmony_ci  long long *slot = tb->slot;
6720f66f451Sopenharmony_ci  char *name, *s, *buf = tb->str, *end = 0;
6730f66f451Sopenharmony_ci  struct sysinfo si;
6740f66f451Sopenharmony_ci  int i, j, fd;
6750f66f451Sopenharmony_ci  off_t len;
6760f66f451Sopenharmony_ci
6770f66f451Sopenharmony_ci  // Recurse one level into /proc children, skip non-numeric entries
6780f66f451Sopenharmony_ci  if (!new->parent)
6790f66f451Sopenharmony_ci    return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
6800f66f451Sopenharmony_ci      |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
6810f66f451Sopenharmony_ci
6820f66f451Sopenharmony_ci  // Grab PID and figure out if we're a thread or a process
6830f66f451Sopenharmony_ci  memset(slot, 0, sizeof(tb->slot));
6840f66f451Sopenharmony_ci  slot[SLOT_tid] = *slot = atol(new->name);
6850f66f451Sopenharmony_ci  if (TT.threadparent && TT.threadparent->extra) {
6860f66f451Sopenharmony_ci    struct procpid *tb2 = (struct procpid *)TT.threadparent->extra;
6870f66f451Sopenharmony_ci
6880f66f451Sopenharmony_ci    *slot = *tb2->slot;
6890f66f451Sopenharmony_ci    // Parent also shows up as a thread, but we need to reread task/stat fields
6900f66f451Sopenharmony_ci    // to get non-collated info for just parent thread (vs whole process).
6910f66f451Sopenharmony_ci    if (*slot == slot[SLOT_tid]) slot = tb2->slot;
6920f66f451Sopenharmony_ci  }
6930f66f451Sopenharmony_ci  fd = dirtree_parentfd(new);
6940f66f451Sopenharmony_ci
6950f66f451Sopenharmony_ci  // Read /proc/$PID/stat into half of toybuf.
6960f66f451Sopenharmony_ci  len = 2048;
6970f66f451Sopenharmony_ci  sprintf(buf, "%lld/stat", slot[SLOT_tid]);
6980f66f451Sopenharmony_ci  if (!readfileat(fd, buf, buf, &len)) return 0;
6990f66f451Sopenharmony_ci
7000f66f451Sopenharmony_ci  // parse oddball fields: the first field is same as new->name (skip it)
7010f66f451Sopenharmony_ci  // and the second and third (name and state) are the only non-numeric fields.
7020f66f451Sopenharmony_ci  // Name has (parentheses) around it, and can have embedded ')' so match
7030f66f451Sopenharmony_ci  // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that).
7040f66f451Sopenharmony_ci  // TODO: kernel task struct actually limits name to 16 chars?
7050f66f451Sopenharmony_ci  if (!(name = strchr(buf, '('))) return 0;
7060f66f451Sopenharmony_ci  for (s = ++name; *s; s++) if (*s == ')') end = s;
7070f66f451Sopenharmony_ci  if (!end || end-name>255) return 0;
7080f66f451Sopenharmony_ci  if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
7090f66f451Sopenharmony_ci
7100f66f451Sopenharmony_ci  // All remaining fields should be numeric, parse them into slot[] array
7110f66f451Sopenharmony_ci  // (skipping first 3 stat fields and first slot[], both were handled above)
7120f66f451Sopenharmony_ci  // yes this means the alignment's off: stat[4] becomes slot[1]
7130f66f451Sopenharmony_ci  for (j = SLOT_ppid; j<SLOT_upticks; j++)
7140f66f451Sopenharmony_ci    if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
7150f66f451Sopenharmony_ci
7160f66f451Sopenharmony_ci  // Now we've read the data, move status and name right after slot[] array,
7170f66f451Sopenharmony_ci  // and convert low chars to ? for non-tty display while we're at it.
7180f66f451Sopenharmony_ci  for (i = 0; i<end-name; i++)
7190f66f451Sopenharmony_ci    if ((tb->str[i] = name[i]) < ' ')
7200f66f451Sopenharmony_ci      if (!TT.tty) tb->str[i] = '?';
7210f66f451Sopenharmony_ci  buf = tb->str+i;
7220f66f451Sopenharmony_ci  *buf++ = 0;
7230f66f451Sopenharmony_ci  len = sizeof(toybuf)-(buf-toybuf);
7240f66f451Sopenharmony_ci
7250f66f451Sopenharmony_ci  // Overwrite useless/obsolete stat fields with more interesting data.
7260f66f451Sopenharmony_ci
7270f66f451Sopenharmony_ci  // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
7280f66f451Sopenharmony_ci  // or numeric wchan, and the remaining two are always zero), and vmlck into
7290f66f451Sopenharmony_ci  // 18 (which is "obsolete, always 0" from stat)
7300f66f451Sopenharmony_ci  slot[SLOT_uid] = new->st.st_uid;
7310f66f451Sopenharmony_ci  slot[SLOT_gid] = new->st.st_gid;
7320f66f451Sopenharmony_ci
7330f66f451Sopenharmony_ci  // TIME and TIME+ use combined value, ksort needs 'em added.
7340f66f451Sopenharmony_ci  slot[SLOT_utime] += slot[SLOT_stime];
7350f66f451Sopenharmony_ci  slot[SLOT_utime2] = slot[SLOT_utime];
7360f66f451Sopenharmony_ci
7370f66f451Sopenharmony_ci  // Do we need to read "status"?
7380f66f451Sopenharmony_ci  if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
7390f66f451Sopenharmony_ci               |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
7400f66f451Sopenharmony_ci  {
7410f66f451Sopenharmony_ci    off_t temp = len;
7420f66f451Sopenharmony_ci
7430f66f451Sopenharmony_ci    sprintf(buf, "%lld/status", slot[SLOT_tid]);
7440f66f451Sopenharmony_ci    if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
7450f66f451Sopenharmony_ci    s = strafter(buf, "\nUid:");
7460f66f451Sopenharmony_ci    slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
7470f66f451Sopenharmony_ci    s = strafter(buf, "\nGid:");
7480f66f451Sopenharmony_ci    slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
7490f66f451Sopenharmony_ci    if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024;
7500f66f451Sopenharmony_ci    if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024;
7510f66f451Sopenharmony_ci  }
7520f66f451Sopenharmony_ci
7530f66f451Sopenharmony_ci  // Do we need to read "io"?
7540f66f451Sopenharmony_ci  if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
7550f66f451Sopenharmony_ci    off_t temp = len;
7560f66f451Sopenharmony_ci
7570f66f451Sopenharmony_ci    sprintf(buf, "%lld/io", slot[SLOT_tid]);
7580f66f451Sopenharmony_ci    if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
7590f66f451Sopenharmony_ci    if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
7600f66f451Sopenharmony_ci    if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
7610f66f451Sopenharmony_ci    if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
7620f66f451Sopenharmony_ci    if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
7630f66f451Sopenharmony_ci    slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
7640f66f451Sopenharmony_ci    slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
7650f66f451Sopenharmony_ci  }
7660f66f451Sopenharmony_ci
7670f66f451Sopenharmony_ci  // If we were updating thread parent with its own task info, we're done.
7680f66f451Sopenharmony_ci  if (slot != tb->slot) return 0;
7690f66f451Sopenharmony_ci
7700f66f451Sopenharmony_ci  // We now know enough to skip processes we don't care about.
7710f66f451Sopenharmony_ci  if (TT.match_process && !TT.match_process(slot)) return 0;
7720f66f451Sopenharmony_ci
7730f66f451Sopenharmony_ci  // /proc data is generated as it's read, so for maximum accuracy on slow
7740f66f451Sopenharmony_ci  // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
7750f66f451Sopenharmony_ci  sysinfo(&si);
7760f66f451Sopenharmony_ci  slot[SLOT_uptime] = si.uptime;
7770f66f451Sopenharmony_ci  slot[SLOT_totalram] = si.totalram;
7780f66f451Sopenharmony_ci  slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
7790f66f451Sopenharmony_ci
7800f66f451Sopenharmony_ci  // Do we need to read "statm"?
7810f66f451Sopenharmony_ci  if (TT.bits&(_PS_VIRT|_PS_SHR)) {
7820f66f451Sopenharmony_ci    off_t temp = len;
7830f66f451Sopenharmony_ci
7840f66f451Sopenharmony_ci    sprintf(buf, "%lld/statm", slot[SLOT_tid]);
7850f66f451Sopenharmony_ci    if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
7860f66f451Sopenharmony_ci
7870f66f451Sopenharmony_ci    // Skip redundant RSS field, we got it from stat.
7880f66f451Sopenharmony_ci    slot[SLOT_vsz] = slot[SLOT_shr] = 0;
7890f66f451Sopenharmony_ci    sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]);
7900f66f451Sopenharmony_ci  }
7910f66f451Sopenharmony_ci
7920f66f451Sopenharmony_ci  // Do we need to read "exe"?
7930f66f451Sopenharmony_ci  if (TT.bits&_PS_BIT) {
7940f66f451Sopenharmony_ci    off_t temp = 6;
7950f66f451Sopenharmony_ci
7960f66f451Sopenharmony_ci    sprintf(buf, "%lld/exe", slot[SLOT_tid]);
7970f66f451Sopenharmony_ci    if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
7980f66f451Sopenharmony_ci      if (buf[4] == 1) slot[SLOT_bits] = 32;
7990f66f451Sopenharmony_ci      else if (buf[4] == 2) slot[SLOT_bits] = 64;
8000f66f451Sopenharmony_ci    }
8010f66f451Sopenharmony_ci  }
8020f66f451Sopenharmony_ci
8030f66f451Sopenharmony_ci  // Do we need Android scheduling policy?
8040f66f451Sopenharmony_ci  if (TT.bits&_PS_PCY)
8050f66f451Sopenharmony_ci    get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]);
8060f66f451Sopenharmony_ci
8070f66f451Sopenharmony_ci  // Done using buf[] (tb->str) as scratch space, now read string data,
8080f66f451Sopenharmony_ci  // saving consective null terminated strings. (Save starting offsets into
8090f66f451Sopenharmony_ci  // str->offset to avoid strlen() loop to find relevant string.)
8100f66f451Sopenharmony_ci
8110f66f451Sopenharmony_ci  // Fetch string data while parentfd still available, appending to buf.
8120f66f451Sopenharmony_ci  // (There's well over 3k of toybuf left. We could dynamically malloc, but
8130f66f451Sopenharmony_ci  // it'd almost never get used, querying length of a proc file is awkward,
8140f66f451Sopenharmony_ci  // fixed buffer is nommu friendly... Wait for somebody to complain. :)
8150f66f451Sopenharmony_ci
8160f66f451Sopenharmony_ci  // The fetch[] array at the start of the function says what file to read
8170f66f451Sopenharmony_ci  // and what -o display field outputs it (to skip the ones we don't need).
8180f66f451Sopenharmony_ci
8190f66f451Sopenharmony_ci  slot[SLOT_argv0len] = 0;
8200f66f451Sopenharmony_ci  for (j = 0; j<ARRAY_LEN(fetch); j++) {
8210f66f451Sopenharmony_ci    tb->offset[j] = buf-(tb->str);
8220f66f451Sopenharmony_ci    if (!(TT.bits&fetch[j].bits)) {
8230f66f451Sopenharmony_ci      *buf++ = 0;
8240f66f451Sopenharmony_ci      continue;
8250f66f451Sopenharmony_ci    }
8260f66f451Sopenharmony_ci
8270f66f451Sopenharmony_ci    // Determine available space: reserve 256 bytes (guaranteed minimum) for
8280f66f451Sopenharmony_ci    // each string we haven't checked yet, tb->str starts after the numeric
8290f66f451Sopenharmony_ci    // arrays in struct procpid, and we reserve 260 bytes scratch space at the
8300f66f451Sopenharmony_ci    // end of toybuf for output conversion in string_field(). Other than that,
8310f66f451Sopenharmony_ci    // each use all available space, and future strings that don't use their
8320f66f451Sopenharmony_ci    // guaranteed minimum add to the pool.
8330f66f451Sopenharmony_ci    len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260;
8340f66f451Sopenharmony_ci    sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
8350f66f451Sopenharmony_ci
8360f66f451Sopenharmony_ci    // For exe (j==3) readlink() instead of reading file's contents
8370f66f451Sopenharmony_ci    // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID).
8380f66f451Sopenharmony_ci    if (j==3 || j==5) {
8390f66f451Sopenharmony_ci      struct procpid *ptb = 0;
8400f66f451Sopenharmony_ci      int k;
8410f66f451Sopenharmony_ci
8420f66f451Sopenharmony_ci      // Thread doesn't have exe or argv[0], so use parent's
8430f66f451Sopenharmony_ci      if (TT.threadparent && TT.threadparent->extra)
8440f66f451Sopenharmony_ci        ptb = (void *)TT.threadparent->extra;
8450f66f451Sopenharmony_ci
8460f66f451Sopenharmony_ci      if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
8470f66f451Sopenharmony_ci      else {
8480f66f451Sopenharmony_ci        if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
8490f66f451Sopenharmony_ci        else {
8500f66f451Sopenharmony_ci          if (!ptb || slot[SLOT_argv0len]) ptb = tb;
8510f66f451Sopenharmony_ci          i = ptb->slot[SLOT_argv0len];
8520f66f451Sopenharmony_ci          s = ptb->str+ptb->offset[4];
8530f66f451Sopenharmony_ci          while (-1!=(k = stridx(s, '/')) && k<i) {
8540f66f451Sopenharmony_ci            s += k+1;
8550f66f451Sopenharmony_ci            i -= k+1;
8560f66f451Sopenharmony_ci          }
8570f66f451Sopenharmony_ci        }
8580f66f451Sopenharmony_ci        if (i<len) len = i;
8590f66f451Sopenharmony_ci        memcpy(buf, s, len);
8600f66f451Sopenharmony_ci        buf[len] = 0;
8610f66f451Sopenharmony_ci      }
8620f66f451Sopenharmony_ci
8630f66f451Sopenharmony_ci    // Turning stat's SLOT_ttynr into a string is an outright heuristic ordeal.
8640f66f451Sopenharmony_ci    } else if (!j) {
8650f66f451Sopenharmony_ci      int rdev = slot[SLOT_ttynr];
8660f66f451Sopenharmony_ci      struct stat st;
8670f66f451Sopenharmony_ci
8680f66f451Sopenharmony_ci      // Call no tty "?" rather than "0:0".
8690f66f451Sopenharmony_ci      strcpy(buf, "?");
8700f66f451Sopenharmony_ci      if (rdev) {
8710f66f451Sopenharmony_ci        // Can we readlink() our way to a name?
8720f66f451Sopenharmony_ci        for (i = 0; i<3; i++) {
8730f66f451Sopenharmony_ci          sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i);
8740f66f451Sopenharmony_ci          if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
8750f66f451Sopenharmony_ci            && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
8760f66f451Sopenharmony_ci              break;
8770f66f451Sopenharmony_ci        }
8780f66f451Sopenharmony_ci
8790f66f451Sopenharmony_ci        // Couldn't find it, try all the tty drivers.
8800f66f451Sopenharmony_ci        if (i == 3) {
8810f66f451Sopenharmony_ci          FILE *fp = fopen("/proc/tty/drivers", "r");
8820f66f451Sopenharmony_ci          int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
8830f66f451Sopenharmony_ci
8840f66f451Sopenharmony_ci          if (fp) {
8850f66f451Sopenharmony_ci            while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
8860f66f451Sopenharmony_ci              // TODO: we could parse the minor range too.
8870f66f451Sopenharmony_ci              if (tty_major == maj) {
8880f66f451Sopenharmony_ci                len = strlen(buf);
8890f66f451Sopenharmony_ci                len += sprintf(buf+len, "%d", min);
8900f66f451Sopenharmony_ci                if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
8910f66f451Sopenharmony_ci                  break;
8920f66f451Sopenharmony_ci              }
8930f66f451Sopenharmony_ci              tty_major = 0;
8940f66f451Sopenharmony_ci            }
8950f66f451Sopenharmony_ci            fclose(fp);
8960f66f451Sopenharmony_ci          }
8970f66f451Sopenharmony_ci
8980f66f451Sopenharmony_ci          // Really couldn't find it, so just show major:minor.
8990f66f451Sopenharmony_ci          if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
9000f66f451Sopenharmony_ci        }
9010f66f451Sopenharmony_ci
9020f66f451Sopenharmony_ci        s = buf;
9030f66f451Sopenharmony_ci        if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
9040f66f451Sopenharmony_ci      }
9050f66f451Sopenharmony_ci
9060f66f451Sopenharmony_ci    // For the rest, the data we want is in a file we can just read.
9070f66f451Sopenharmony_ci    } else {
9080f66f451Sopenharmony_ci      int temp = 0;
9090f66f451Sopenharmony_ci
9100f66f451Sopenharmony_ci      // When command has no arguments, don't space over the NUL
9110f66f451Sopenharmony_ci      if (readfileat(fd, buf, buf, &len) && len>0) {
9120f66f451Sopenharmony_ci
9130f66f451Sopenharmony_ci        // Trim trailing whitespace and NUL bytes
9140f66f451Sopenharmony_ci        while (len)
9150f66f451Sopenharmony_ci          if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
9160f66f451Sopenharmony_ci          else break;
9170f66f451Sopenharmony_ci
9180f66f451Sopenharmony_ci        // Turn NUL to space, other low ascii to ? (in non-tty mode), except
9190f66f451Sopenharmony_ci        // cmdline has a trailing NUL that we don't want to turn to space.
9200f66f451Sopenharmony_ci        for (i=0; i<len-1; i++) {
9210f66f451Sopenharmony_ci          char c = buf[i];
9220f66f451Sopenharmony_ci
9230f66f451Sopenharmony_ci          if (!c) {
9240f66f451Sopenharmony_ci            if (!temp) temp = i;
9250f66f451Sopenharmony_ci            c = ' ';
9260f66f451Sopenharmony_ci          } else if (!TT.tty && c<' ') c = '?';
9270f66f451Sopenharmony_ci          buf[i] = c;
9280f66f451Sopenharmony_ci        }
9290f66f451Sopenharmony_ci      } else *buf = len = 0;
9300f66f451Sopenharmony_ci
9310f66f451Sopenharmony_ci      // Store end of argv[0] so ARGS and CMDLINE can differ.
9320f66f451Sopenharmony_ci      // We do it for each file string slot but last is cmdline, which sticks.
9330f66f451Sopenharmony_ci      slot[SLOT_argv0len] = temp ? temp : len;  // Position of _first_ NUL
9340f66f451Sopenharmony_ci    }
9350f66f451Sopenharmony_ci
9360f66f451Sopenharmony_ci    // Each case above calculated/retained len, so we don't need to re-strlen.
9370f66f451Sopenharmony_ci    buf += len+1;
9380f66f451Sopenharmony_ci  }
9390f66f451Sopenharmony_ci
9400f66f451Sopenharmony_ci  // Record that we saw another process, and display/return now if appropriate
9410f66f451Sopenharmony_ci  TT.kcount++;
9420f66f451Sopenharmony_ci  if (TT.show_process && !TT.threadparent) {
9430f66f451Sopenharmony_ci    TT.show_process(tb);
9440f66f451Sopenharmony_ci
9450f66f451Sopenharmony_ci    return 0;
9460f66f451Sopenharmony_ci  }
9470f66f451Sopenharmony_ci
9480f66f451Sopenharmony_ci  // We're retaining data (probably to sort it), save copy in list.
9490f66f451Sopenharmony_ci  s = xmalloc(buf-toybuf);
9500f66f451Sopenharmony_ci  new->extra = (long)s;
9510f66f451Sopenharmony_ci  memcpy(s, toybuf, buf-toybuf);
9520f66f451Sopenharmony_ci
9530f66f451Sopenharmony_ci  return DIRTREE_SAVE;
9540f66f451Sopenharmony_ci}
9550f66f451Sopenharmony_ci
9560f66f451Sopenharmony_ci// wrapper for get_ps() that also collects threads under each processes
9570f66f451Sopenharmony_cistatic int get_threads(struct dirtree *new)
9580f66f451Sopenharmony_ci{
9590f66f451Sopenharmony_ci  struct dirtree *dt;
9600f66f451Sopenharmony_ci  struct procpid *tb;
9610f66f451Sopenharmony_ci  unsigned pid, kcount;
9620f66f451Sopenharmony_ci
9630f66f451Sopenharmony_ci  if (!new->parent) return get_ps(new);
9640f66f451Sopenharmony_ci  pid = atol(new->name);
9650f66f451Sopenharmony_ci
9660f66f451Sopenharmony_ci  TT.threadparent = new;
9670f66f451Sopenharmony_ci  if (!get_ps(new)) {
9680f66f451Sopenharmony_ci    // it exited out from under us
9690f66f451Sopenharmony_ci    TT.threadparent = 0;
9700f66f451Sopenharmony_ci
9710f66f451Sopenharmony_ci    return 0;
9720f66f451Sopenharmony_ci  }
9730f66f451Sopenharmony_ci
9740f66f451Sopenharmony_ci  // Recurse down into tasks, retaining thread groups.
9750f66f451Sopenharmony_ci  // Disable show_process at least until we can calculate tcount
9760f66f451Sopenharmony_ci  kcount = TT.kcount;
9770f66f451Sopenharmony_ci  sprintf(toybuf, "/proc/%u/task", pid);
9780f66f451Sopenharmony_ci  new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
9790f66f451Sopenharmony_ci  if (new->child == DIRTREE_ABORTVAL) new->child = 0;
9800f66f451Sopenharmony_ci  TT.threadparent = 0;
9810f66f451Sopenharmony_ci  kcount = TT.kcount-kcount+1;
9820f66f451Sopenharmony_ci  tb = (void *)new->extra;
9830f66f451Sopenharmony_ci  tb->slot[SLOT_tcount] = kcount;
9840f66f451Sopenharmony_ci
9850f66f451Sopenharmony_ci  // Fill out tid and thread count for each entry in group (if it didn't exit
9860f66f451Sopenharmony_ci  // out from under us again; asynchronous reads of unlocked data are fun!)
9870f66f451Sopenharmony_ci  if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
9880f66f451Sopenharmony_ci    tb = (void *)dt->extra;
9890f66f451Sopenharmony_ci    tb->slot[SLOT_pid] = pid;
9900f66f451Sopenharmony_ci    tb->slot[SLOT_tcount] = kcount;
9910f66f451Sopenharmony_ci  }
9920f66f451Sopenharmony_ci
9930f66f451Sopenharmony_ci  // Save or display
9940f66f451Sopenharmony_ci  if (!TT.show_process) return DIRTREE_SAVE;
9950f66f451Sopenharmony_ci  TT.show_process((void *)new->extra);
9960f66f451Sopenharmony_ci  if ((dt = new->child)) {
9970f66f451Sopenharmony_ci    new->child = 0;
9980f66f451Sopenharmony_ci    while (dt->child) {
9990f66f451Sopenharmony_ci      new = dt->child->next;
10000f66f451Sopenharmony_ci      TT.show_process((void *)dt->child->extra);
10010f66f451Sopenharmony_ci      free(dt->child);
10020f66f451Sopenharmony_ci      dt->child = new;
10030f66f451Sopenharmony_ci    }
10040f66f451Sopenharmony_ci    free(dt);
10050f66f451Sopenharmony_ci  }
10060f66f451Sopenharmony_ci
10070f66f451Sopenharmony_ci  return 0;
10080f66f451Sopenharmony_ci}
10090f66f451Sopenharmony_ci
10100f66f451Sopenharmony_ci// Parse one FIELD argument (with optional =name :width) into struct ofields
10110f66f451Sopenharmony_cistatic char *parse_ko(void *data, char *type, int length)
10120f66f451Sopenharmony_ci{
10130f66f451Sopenharmony_ci  struct ofields *field;
10140f66f451Sopenharmony_ci  char *width, *title, *end, *s;
10150f66f451Sopenharmony_ci  int i, j, k;
10160f66f451Sopenharmony_ci
10170f66f451Sopenharmony_ci  // Caller's WOULD_EXIT catches -o help and prints help
10180f66f451Sopenharmony_ci  if (length==4 && !strncasecmp(type, "HELP", length)) xexit();
10190f66f451Sopenharmony_ci
10200f66f451Sopenharmony_ci  // Get title, length of title, type, end of type, and display width
10210f66f451Sopenharmony_ci
10220f66f451Sopenharmony_ci  // Chip off =name to display
10230f66f451Sopenharmony_ci  if ((end = strchr(type, '=')) && length>(end-type)) {
10240f66f451Sopenharmony_ci    title = end+1;
10250f66f451Sopenharmony_ci    length -= (end-type)+1;
10260f66f451Sopenharmony_ci  } else {
10270f66f451Sopenharmony_ci    end = type+length;
10280f66f451Sopenharmony_ci    title = 0;
10290f66f451Sopenharmony_ci  }
10300f66f451Sopenharmony_ci
10310f66f451Sopenharmony_ci  // Chip off :width to display
10320f66f451Sopenharmony_ci  if ((width = strchr(type, ':')) && width<end) {
10330f66f451Sopenharmony_ci    if (!title) length = width-type;
10340f66f451Sopenharmony_ci  } else width = 0;
10350f66f451Sopenharmony_ci
10360f66f451Sopenharmony_ci  // Allocate structure plus extra space to append a copy of title data
10370f66f451Sopenharmony_ci  // (this way it's same lifetime, freeing struct automatically frees title)
10380f66f451Sopenharmony_ci  field = xzalloc(sizeof(struct ofields)+(length+1)*!!title);
10390f66f451Sopenharmony_ci  if (title) {
10400f66f451Sopenharmony_ci    memcpy(field->title = (char *)(field+1), title, length);
10410f66f451Sopenharmony_ci    field->title[field->len = length] = 0;
10420f66f451Sopenharmony_ci  }
10430f66f451Sopenharmony_ci
10440f66f451Sopenharmony_ci  if (width) {
10450f66f451Sopenharmony_ci    field->len = strtol(++width, &title, 10);
10460f66f451Sopenharmony_ci    if (!isdigit(*width) || title != end) return title;
10470f66f451Sopenharmony_ci    end = --width;
10480f66f451Sopenharmony_ci  }
10490f66f451Sopenharmony_ci
10500f66f451Sopenharmony_ci  // Find type
10510f66f451Sopenharmony_ci  field->reverse = 1;
10520f66f451Sopenharmony_ci  if (*type == '-') field->reverse = -1;
10530f66f451Sopenharmony_ci  else if (*type != '+') type--;
10540f66f451Sopenharmony_ci  type++;
10550f66f451Sopenharmony_ci  for (i = 0; i<ARRAY_LEN(typos); i++) {
10560f66f451Sopenharmony_ci    field->which = i;
10570f66f451Sopenharmony_ci    for (j = 0; j<2; j++) {
10580f66f451Sopenharmony_ci      if (!j) s = typos[i].name;
10590f66f451Sopenharmony_ci      // posix requires alternate names for some fields
10600f66f451Sopenharmony_ci      else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
10610f66f451Sopenharmony_ci        PS_VSZ, PS_USER, 0}, i))) continue;
10620f66f451Sopenharmony_ci      else
10630f66f451Sopenharmony_ci        s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
10640f66f451Sopenharmony_ci
10650f66f451Sopenharmony_ci      if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
10660f66f451Sopenharmony_ci    }
10670f66f451Sopenharmony_ci    if (j!=2) break;
10680f66f451Sopenharmony_ci  }
10690f66f451Sopenharmony_ci  if (i==ARRAY_LEN(typos)) return type;
10700f66f451Sopenharmony_ci  if (!field->title) field->title = typos[field->which].name;
10710f66f451Sopenharmony_ci  if (!field->len) field->len = typos[field->which].width;
10720f66f451Sopenharmony_ci  else if (typos[field->which].width<0) field->len *= -1;
10730f66f451Sopenharmony_ci  dlist_add_nomalloc(data, (void *)field);
10740f66f451Sopenharmony_ci
10750f66f451Sopenharmony_ci  return 0;
10760f66f451Sopenharmony_ci}
10770f66f451Sopenharmony_ci
10780f66f451Sopenharmony_ci// Write FIELD list into display header string (truncating at blen),
10790f66f451Sopenharmony_ci// and return bitfield of which FIELDs are used.
10800f66f451Sopenharmony_cistatic long long get_headers(struct ofields *field, char *buf, int blen)
10810f66f451Sopenharmony_ci{
10820f66f451Sopenharmony_ci  long long bits = 0;
10830f66f451Sopenharmony_ci  int len = 0;
10840f66f451Sopenharmony_ci
10850f66f451Sopenharmony_ci  for (; field; field = field->next) {
10860f66f451Sopenharmony_ci    len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
10870f66f451Sopenharmony_ci      field->title);
10880f66f451Sopenharmony_ci    bits |= 1LL<<field->which;
10890f66f451Sopenharmony_ci  }
10900f66f451Sopenharmony_ci
10910f66f451Sopenharmony_ci  return bits;
10920f66f451Sopenharmony_ci}
10930f66f451Sopenharmony_ci
10940f66f451Sopenharmony_ci// Parse command line options -p -s -t -u -U -g -G
10950f66f451Sopenharmony_cistatic char *parse_rest(void *data, char *str, int len)
10960f66f451Sopenharmony_ci{
10970f66f451Sopenharmony_ci  struct ptr_len *pl = (struct ptr_len *)data;
10980f66f451Sopenharmony_ci  long *ll = pl->ptr;
10990f66f451Sopenharmony_ci  char *end;
11000f66f451Sopenharmony_ci  int num = 0;
11010f66f451Sopenharmony_ci
11020f66f451Sopenharmony_ci  // Allocate next chunk of data
11030f66f451Sopenharmony_ci  if (!(15&pl->len))
11040f66f451Sopenharmony_ci    ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
11050f66f451Sopenharmony_ci
11060f66f451Sopenharmony_ci  // Parse numerical input
11070f66f451Sopenharmony_ci  if (isdigit(*str)) {
11080f66f451Sopenharmony_ci    ll[pl->len] = xstrtol(str, &end, 10);
11090f66f451Sopenharmony_ci    if (end==(len+str)) num++;
11100f66f451Sopenharmony_ci    // For pkill, -s 0 represents pkill's session id.
11110f66f451Sopenharmony_ci    if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
11120f66f451Sopenharmony_ci  }
11130f66f451Sopenharmony_ci
11140f66f451Sopenharmony_ci  if (pl==&TT.pp || pl==&TT.ss) {
11150f66f451Sopenharmony_ci    if (num && ll[pl->len]>0) {
11160f66f451Sopenharmony_ci      pl->len++;
11170f66f451Sopenharmony_ci
11180f66f451Sopenharmony_ci      return 0;
11190f66f451Sopenharmony_ci    }
11200f66f451Sopenharmony_ci  } else if (pl==&TT.tt) {
11210f66f451Sopenharmony_ci    // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
11220f66f451Sopenharmony_ci    if (!num) {
11230f66f451Sopenharmony_ci      if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
11240f66f451Sopenharmony_ci      if (strstart(&str, "pts/")) {
11250f66f451Sopenharmony_ci        len -= 4;
11260f66f451Sopenharmony_ci        num++;
11270f66f451Sopenharmony_ci      } else if (strstart(&str, "tty")) len -= 3;
11280f66f451Sopenharmony_ci    }
11290f66f451Sopenharmony_ci    if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
11300f66f451Sopenharmony_ci      struct stat st;
11310f66f451Sopenharmony_ci
11320f66f451Sopenharmony_ci      end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
11330f66f451Sopenharmony_ci      memcpy(end, str, len);
11340f66f451Sopenharmony_ci      end[len] = 0;
11350f66f451Sopenharmony_ci      xstat(toybuf, &st);
11360f66f451Sopenharmony_ci      ll[pl->len++] = st.st_rdev;
11370f66f451Sopenharmony_ci
11380f66f451Sopenharmony_ci      return 0;
11390f66f451Sopenharmony_ci    }
11400f66f451Sopenharmony_ci  } else if (len<255) {
11410f66f451Sopenharmony_ci    char name[256];
11420f66f451Sopenharmony_ci
11430f66f451Sopenharmony_ci    if (num) {
11440f66f451Sopenharmony_ci      pl->len++;
11450f66f451Sopenharmony_ci
11460f66f451Sopenharmony_ci      return 0;
11470f66f451Sopenharmony_ci    }
11480f66f451Sopenharmony_ci
11490f66f451Sopenharmony_ci    memcpy(name, str, len);
11500f66f451Sopenharmony_ci    name[len] = 0;
11510f66f451Sopenharmony_ci    if (pl==&TT.gg || pl==&TT.GG) {
11520f66f451Sopenharmony_ci      struct group *gr = getgrnam(name);
11530f66f451Sopenharmony_ci      if (gr) {
11540f66f451Sopenharmony_ci        ll[pl->len++] = gr->gr_gid;
11550f66f451Sopenharmony_ci
11560f66f451Sopenharmony_ci        return 0;
11570f66f451Sopenharmony_ci      }
11580f66f451Sopenharmony_ci    } else if (pl==&TT.uu || pl==&TT.UU) {
11590f66f451Sopenharmony_ci      struct passwd *pw = getpwnam(name);
11600f66f451Sopenharmony_ci      if (pw) {
11610f66f451Sopenharmony_ci        ll[pl->len++] = pw->pw_uid;
11620f66f451Sopenharmony_ci
11630f66f451Sopenharmony_ci        return 0;
11640f66f451Sopenharmony_ci      }
11650f66f451Sopenharmony_ci    }
11660f66f451Sopenharmony_ci  }
11670f66f451Sopenharmony_ci
11680f66f451Sopenharmony_ci  // Return error
11690f66f451Sopenharmony_ci  return str;
11700f66f451Sopenharmony_ci}
11710f66f451Sopenharmony_ci
11720f66f451Sopenharmony_ci// sort processes by FIELD(s) listed in option -k
11730f66f451Sopenharmony_cistatic int ksort(void *aa, void *bb)
11740f66f451Sopenharmony_ci{
11750f66f451Sopenharmony_ci  struct ofields *field;
11760f66f451Sopenharmony_ci  struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
11770f66f451Sopenharmony_ci  int ret = 0, slot;
11780f66f451Sopenharmony_ci
11790f66f451Sopenharmony_ci  for (field = TT.kfields; field && !ret; field = field->next) {
11800f66f451Sopenharmony_ci    slot = typos[field->which].slot;
11810f66f451Sopenharmony_ci
11820f66f451Sopenharmony_ci    // Can we do numeric sort?
11830f66f451Sopenharmony_ci    if (!(slot&XX)) {
11840f66f451Sopenharmony_ci      if (ta->slot[slot]<tb->slot[slot]) ret = -1;
11850f66f451Sopenharmony_ci      if (ta->slot[slot]>tb->slot[slot]) ret = 1;
11860f66f451Sopenharmony_ci    }
11870f66f451Sopenharmony_ci
11880f66f451Sopenharmony_ci    // fallback to string sort
11890f66f451Sopenharmony_ci    if (!ret) {
11900f66f451Sopenharmony_ci      memccpy(toybuf, string_field(ta, field), 0, 2048);
11910f66f451Sopenharmony_ci      toybuf[2048] = 0;
11920f66f451Sopenharmony_ci      ret = strcmp(toybuf, string_field(tb, field));
11930f66f451Sopenharmony_ci    }
11940f66f451Sopenharmony_ci    ret *= field->reverse;
11950f66f451Sopenharmony_ci  }
11960f66f451Sopenharmony_ci
11970f66f451Sopenharmony_ci  return ret;
11980f66f451Sopenharmony_ci}
11990f66f451Sopenharmony_ci
12000f66f451Sopenharmony_ci// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array
12010f66f451Sopenharmony_ci// (recursion because tree from get_thread() isn't flat list of siblings)
12020f66f451Sopenharmony_cistatic struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
12030f66f451Sopenharmony_ci{
12040f66f451Sopenharmony_ci  while (dt) {
12050f66f451Sopenharmony_ci    struct dirtree *next = dt->next;
12060f66f451Sopenharmony_ci
12070f66f451Sopenharmony_ci    if (dt->extra) *(tb++) = (void *)dt->extra;
12080f66f451Sopenharmony_ci    if (dt->child) tb = collate_leaves(tb, dt->child);
12090f66f451Sopenharmony_ci    free(dt);
12100f66f451Sopenharmony_ci    dt = next;
12110f66f451Sopenharmony_ci  }
12120f66f451Sopenharmony_ci
12130f66f451Sopenharmony_ci  return tb;
12140f66f451Sopenharmony_ci}
12150f66f451Sopenharmony_ci
12160f66f451Sopenharmony_ci// Allocate struct procpid array of length count and populate it with ->extra
12170f66f451Sopenharmony_ci// fields from dirtree leaf nodes. (top diffs old & new array to show changes)
12180f66f451Sopenharmony_cistatic struct procpid **collate(int count, struct dirtree *dt)
12190f66f451Sopenharmony_ci{
12200f66f451Sopenharmony_ci  struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
12210f66f451Sopenharmony_ci
12220f66f451Sopenharmony_ci  collate_leaves(tbsort, dt);
12230f66f451Sopenharmony_ci
12240f66f451Sopenharmony_ci  return tbsort;
12250f66f451Sopenharmony_ci}
12260f66f451Sopenharmony_ci
12270f66f451Sopenharmony_ci// parse command line arguments (ala -k -o) with a comma separated FIELD list
12280f66f451Sopenharmony_cistatic void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
12290f66f451Sopenharmony_ci{
12300f66f451Sopenharmony_ci  struct arg_list def;
12310f66f451Sopenharmony_ci  int x;
12320f66f451Sopenharmony_ci
12330f66f451Sopenharmony_ci  memset(&def, 0, sizeof(struct arg_list));
12340f66f451Sopenharmony_ci  def.arg = s;
12350f66f451Sopenharmony_ci  WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
12360f66f451Sopenharmony_ci  if (x) help_help();
12370f66f451Sopenharmony_ci}
12380f66f451Sopenharmony_ci
12390f66f451Sopenharmony_ci// There link ps & top to shell cmd "task".
12400f66f451Sopenharmony_cistatic void link_shell_cmd_task()
12410f66f451Sopenharmony_ci{
12420f66f451Sopenharmony_ci  const char *cmdName = "task";
12430f66f451Sopenharmony_ci  char *cmdLine = NULL;
12440f66f451Sopenharmony_ci
12450f66f451Sopenharmony_ci  if ( toys.argv[1] && !strcmp(toys.argv[1], "-a")) {
12460f66f451Sopenharmony_ci    cmdLine = "task -a";
12470f66f451Sopenharmony_ci    (void)syscall(__NR_shellexec, cmdName, cmdLine);
12480f66f451Sopenharmony_ci  }
12490f66f451Sopenharmony_ci  else
12500f66f451Sopenharmony_ci  {
12510f66f451Sopenharmony_ci    cmdLine = "task";
12520f66f451Sopenharmony_ci    (void)syscall(__NR_shellexec, cmdName, cmdLine);
12530f66f451Sopenharmony_ci  }
12540f66f451Sopenharmony_ci}
12550f66f451Sopenharmony_civoid ps_main(void)
12560f66f451Sopenharmony_ci{
12570f66f451Sopenharmony_ci  link_shell_cmd_task();
12580f66f451Sopenharmony_ci}
12590f66f451Sopenharmony_ci
12600f66f451Sopenharmony_ci#define CLEANUP_ps
12610f66f451Sopenharmony_ci#define FOR_top
12620f66f451Sopenharmony_ci#include "generated/flags.h"
12630f66f451Sopenharmony_ci
12640f66f451Sopenharmony_ci// select which of the -o fields to sort by
12650f66f451Sopenharmony_cistatic void setsort(int pos)
12660f66f451Sopenharmony_ci{
12670f66f451Sopenharmony_ci  struct ofields *field, *field2;
12680f66f451Sopenharmony_ci  int i = 0;
12690f66f451Sopenharmony_ci
12700f66f451Sopenharmony_ci  if (pos<0) pos = 0;
12710f66f451Sopenharmony_ci
12720f66f451Sopenharmony_ci  for (field = TT.fields; field; field = field->next) {
12730f66f451Sopenharmony_ci    if ((TT.sortpos = i++)<pos && field->next) continue;
12740f66f451Sopenharmony_ci    field2 = TT.kfields;
12750f66f451Sopenharmony_ci    field2->which = field->which;
12760f66f451Sopenharmony_ci    field2->len = field->len;
12770f66f451Sopenharmony_ci    break;
12780f66f451Sopenharmony_ci  }
12790f66f451Sopenharmony_ci}
12800f66f451Sopenharmony_ci
12810f66f451Sopenharmony_ci// If we have both, adjust slot[deltas[]] to be relative to previous
12820f66f451Sopenharmony_ci// measurement rather than process start. Stomping old.data is fine
12830f66f451Sopenharmony_ci// because we free it after displaying.
12840f66f451Sopenharmony_cistatic int merge_deltas(long long *oslot, long long *nslot, int milis)
12850f66f451Sopenharmony_ci{
12860f66f451Sopenharmony_ci  char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
12870f66f451Sopenharmony_ci                   SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
12880f66f451Sopenharmony_ci  int i;
12890f66f451Sopenharmony_ci
12900f66f451Sopenharmony_ci  for (i = 0; i<ARRAY_LEN(deltas); i++)
12910f66f451Sopenharmony_ci    oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
12920f66f451Sopenharmony_ci  oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
12930f66f451Sopenharmony_ci
12940f66f451Sopenharmony_ci  return 1;
12950f66f451Sopenharmony_ci}
12960f66f451Sopenharmony_ci
12970f66f451Sopenharmony_cistatic int header_line(int line, int rev)
12980f66f451Sopenharmony_ci{
12990f66f451Sopenharmony_ci  if (!line) return 0;
13000f66f451Sopenharmony_ci
13010f66f451Sopenharmony_ci  if (FLAG(b)) puts(toybuf);
13020f66f451Sopenharmony_ci  else {
13030f66f451Sopenharmony_ci    printf("%s%-*.*s%s\r\n", rev?"\033[7m":"", rev?TT.width:0, TT.width, toybuf,
13040f66f451Sopenharmony_ci      rev?"\033[0m":"");
13050f66f451Sopenharmony_ci  }
13060f66f451Sopenharmony_ci
13070f66f451Sopenharmony_ci  return line-1;
13080f66f451Sopenharmony_ci}
13090f66f451Sopenharmony_ci
13100f66f451Sopenharmony_cistatic void top_cursor_cleanup(void)
13110f66f451Sopenharmony_ci{
13120f66f451Sopenharmony_ci  tty_esc("?25h");
13130f66f451Sopenharmony_ci}
13140f66f451Sopenharmony_ci
13150f66f451Sopenharmony_cistatic void top_common(
13160f66f451Sopenharmony_ci  int (*filter)(long long *oslot, long long *nslot, int milis))
13170f66f451Sopenharmony_ci{
13180f66f451Sopenharmony_ci  long long timeout = 0, now, stats[16];
13190f66f451Sopenharmony_ci  struct proclist {
13200f66f451Sopenharmony_ci    struct procpid **tb;
13210f66f451Sopenharmony_ci    int count;
13220f66f451Sopenharmony_ci    long long whence;
13230f66f451Sopenharmony_ci  } plist[2], *plold, *plnew, old, new, mix;
13240f66f451Sopenharmony_ci  char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
13250f66f451Sopenharmony_ci    "iow", "irq", "sirq", "host"};
13260f66f451Sopenharmony_ci  unsigned tock = 0;
13270f66f451Sopenharmony_ci  int i, lines, topoff = 0, done = 0;
13280f66f451Sopenharmony_ci  char stdout_buf[BUFSIZ];
13290f66f451Sopenharmony_ci
13300f66f451Sopenharmony_ci  if (!TT.fields) perror_exit("no -o");
13310f66f451Sopenharmony_ci
13320f66f451Sopenharmony_ci  // Avoid flicker and hide the cursor in interactive mode.
13330f66f451Sopenharmony_ci  if (!FLAG(b)) {
13340f66f451Sopenharmony_ci    setbuf(stdout, stdout_buf);
13350f66f451Sopenharmony_ci    sigatexit(top_cursor_cleanup);
13360f66f451Sopenharmony_ci    tty_esc("?25l");
13370f66f451Sopenharmony_ci  }
13380f66f451Sopenharmony_ci
13390f66f451Sopenharmony_ci  toys.signal = SIGWINCH;
13400f66f451Sopenharmony_ci  TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
13410f66f451Sopenharmony_ci  *scratch = 0;
13420f66f451Sopenharmony_ci  memset(plist, 0, sizeof(plist));
13430f66f451Sopenharmony_ci  memset(stats, 0, sizeof(stats));
13440f66f451Sopenharmony_ci  do {
13450f66f451Sopenharmony_ci    struct dirtree *dt;
13460f66f451Sopenharmony_ci    int recalc = 1;
13470f66f451Sopenharmony_ci
13480f66f451Sopenharmony_ci    plold = plist+(tock++&1);
13490f66f451Sopenharmony_ci    plnew = plist+(tock&1);
13500f66f451Sopenharmony_ci    plnew->whence = millitime();
13510f66f451Sopenharmony_ci    dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
13520f66f451Sopenharmony_ci      (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps);
13530f66f451Sopenharmony_ci    if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
13540f66f451Sopenharmony_ci    plnew->tb = collate(plnew->count = TT.kcount, dt);
13550f66f451Sopenharmony_ci    TT.kcount = 0;
13560f66f451Sopenharmony_ci
13570f66f451Sopenharmony_ci    if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
13580f66f451Sopenharmony_ci      long long *st = stats+8*(tock&1);
13590f66f451Sopenharmony_ci
13600f66f451Sopenharmony_ci      // user nice system idle iowait irq softirq host
13610f66f451Sopenharmony_ci      sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
13620f66f451Sopenharmony_ci        st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
13630f66f451Sopenharmony_ci    }
13640f66f451Sopenharmony_ci
13650f66f451Sopenharmony_ci    // First time, wait a quarter of a second to collect a little delta data.
13660f66f451Sopenharmony_ci    if (!plold->tb) {
13670f66f451Sopenharmony_ci      msleep(250);
13680f66f451Sopenharmony_ci      continue;
13690f66f451Sopenharmony_ci    }
13700f66f451Sopenharmony_ci
13710f66f451Sopenharmony_ci    // Collate old and new into "mix", depends on /proc read in pid sort order
13720f66f451Sopenharmony_ci    old = *plold;
13730f66f451Sopenharmony_ci    new = *plnew;
13740f66f451Sopenharmony_ci    mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
13750f66f451Sopenharmony_ci    mix.count = 0;
13760f66f451Sopenharmony_ci
13770f66f451Sopenharmony_ci    while (old.count || new.count) {
13780f66f451Sopenharmony_ci      struct procpid *otb = old.count ? *old.tb : 0,
13790f66f451Sopenharmony_ci                     *ntb = new.count ? *new.tb : 0;
13800f66f451Sopenharmony_ci
13810f66f451Sopenharmony_ci      // If we just have old for this process, it exited. Discard it.
13820f66f451Sopenharmony_ci      if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
13830f66f451Sopenharmony_ci        old.tb++;
13840f66f451Sopenharmony_ci        old.count--;
13850f66f451Sopenharmony_ci
13860f66f451Sopenharmony_ci        continue;
13870f66f451Sopenharmony_ci      }
13880f66f451Sopenharmony_ci
13890f66f451Sopenharmony_ci      // If we just have new, use it verbatim
13900f66f451Sopenharmony_ci      if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
13910f66f451Sopenharmony_ci      else {
13920f66f451Sopenharmony_ci        // Keep or discard
13930f66f451Sopenharmony_ci        if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
13940f66f451Sopenharmony_ci          mix.tb[mix.count] = otb;
13950f66f451Sopenharmony_ci          mix.count++;
13960f66f451Sopenharmony_ci        }
13970f66f451Sopenharmony_ci        old.tb++;
13980f66f451Sopenharmony_ci        old.count--;
13990f66f451Sopenharmony_ci      }
14000f66f451Sopenharmony_ci      new.tb++;
14010f66f451Sopenharmony_ci      new.count--;
14020f66f451Sopenharmony_ci    }
14030f66f451Sopenharmony_ci
14040f66f451Sopenharmony_ci    // Don't re-fetch data if it's not time yet, just re-display existing data.
14050f66f451Sopenharmony_ci    for (;;) {
14060f66f451Sopenharmony_ci      char was, is;
14070f66f451Sopenharmony_ci
14080f66f451Sopenharmony_ci      if (recalc) {
14090f66f451Sopenharmony_ci        qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
14100f66f451Sopenharmony_ci        if (!FLAG(b)) {
14110f66f451Sopenharmony_ci          printf("\033[H\033[J");
14120f66f451Sopenharmony_ci          if (toys.signal) {
14130f66f451Sopenharmony_ci            toys.signal = 0;
14140f66f451Sopenharmony_ci            terminal_probesize(&TT.width, &TT.height);
14150f66f451Sopenharmony_ci          }
14160f66f451Sopenharmony_ci        }
14170f66f451Sopenharmony_ci        if (TT.top.m) TT.height = TT.top.m+5;
14180f66f451Sopenharmony_ci        lines = TT.height;
14190f66f451Sopenharmony_ci      }
14200f66f451Sopenharmony_ci      if (recalc && !FLAG(q)) {
14210f66f451Sopenharmony_ci        // Display "top" header.
14220f66f451Sopenharmony_ci        if (*toys.which->name == 't') {
14230f66f451Sopenharmony_ci          struct ofields field;
14240f66f451Sopenharmony_ci          char hr[4][32];
14250f66f451Sopenharmony_ci          long long ll, up = 0;
14260f66f451Sopenharmony_ci          long run[6];
14270f66f451Sopenharmony_ci          int j;
14280f66f451Sopenharmony_ci
14290f66f451Sopenharmony_ci          // Count running, sleeping, stopped, zombie processes.
14300f66f451Sopenharmony_ci          // The kernel has more states (and different sets in different
14310f66f451Sopenharmony_ci          // versions), so we need to map them. (R)unning and (Z)ombie are
14320f66f451Sopenharmony_ci          // easy enough, and since "stopped" is rare (just T and t as of
14330f66f451Sopenharmony_ci          // Linux 4.20), we assume everything else is "sleeping".
14340f66f451Sopenharmony_ci          field.which = PS_S;
14350f66f451Sopenharmony_ci          memset(run, 0, sizeof(run));
14360f66f451Sopenharmony_ci          for (i = 0; i<mix.count; i++)
14370f66f451Sopenharmony_ci            run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++;
14380f66f451Sopenharmony_ci          sprintf(toybuf,
14390f66f451Sopenharmony_ci            "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, "
14400f66f451Sopenharmony_ci            "%3ld zombie", FLAG(H)?"Thread":"Task", mix.count, run[1], run[0],
14410f66f451Sopenharmony_ci            run[2]+run[3], run[4]);
14420f66f451Sopenharmony_ci          lines = header_line(lines, 0);
14430f66f451Sopenharmony_ci
14440f66f451Sopenharmony_ci          if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) {
14450f66f451Sopenharmony_ci            for (i = 0; i<6; i++) {
14460f66f451Sopenharmony_ci              j = i%3;
14470f66f451Sopenharmony_ci              pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:",
14480f66f451Sopenharmony_ci                    "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]);
14490f66f451Sopenharmony_ci              human_readable_long(hr[j+!!j], 1024*(run[i] = pos?atol(pos):0),
14500f66f451Sopenharmony_ci                8, 0);
14510f66f451Sopenharmony_ci              if (j==1) human_readable_long(hr[1], 1024*(run[i-1]-run[i]), 8,0);
14520f66f451Sopenharmony_ci              else if (j==2) {
14530f66f451Sopenharmony_ci                sprintf(toybuf, (i<3)
14540f66f451Sopenharmony_ci                  ? "  Mem: %9s total, %9s used, %9s free, %9s buffers"
14550f66f451Sopenharmony_ci                  : " Swap: %9s total, %9s used, %9s free, %9s cached",
14560f66f451Sopenharmony_ci                  hr[0], hr[1], hr[2], hr[3]);
14570f66f451Sopenharmony_ci                lines = header_line(lines, 0);
14580f66f451Sopenharmony_ci              }
14590f66f451Sopenharmony_ci            }
14600f66f451Sopenharmony_ci          }
14610f66f451Sopenharmony_ci
14620f66f451Sopenharmony_ci          pos = toybuf;
14630f66f451Sopenharmony_ci          i = sysconf(_SC_NPROCESSORS_CONF);
14640f66f451Sopenharmony_ci          pos += sprintf(pos, "%d%%cpu", i*100);
14650f66f451Sopenharmony_ci          j = 4+(i>10);
14660f66f451Sopenharmony_ci
14670f66f451Sopenharmony_ci          // If a processor goes idle it's powered down and its idle ticks don't
14680f66f451Sopenharmony_ci          // advance, so calculate idle time as potential time - used.
14690f66f451Sopenharmony_ci          if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
14700f66f451Sopenharmony_ci          if (!up) up = 1;
14710f66f451Sopenharmony_ci          now = up*i;
14720f66f451Sopenharmony_ci          ll = stats[3] = stats[11] = 0;
14730f66f451Sopenharmony_ci          for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
14740f66f451Sopenharmony_ci          stats[3] = now - llabs(ll);
14750f66f451Sopenharmony_ci
14760f66f451Sopenharmony_ci          for (i = 0; i<8; i++) {
14770f66f451Sopenharmony_ci            ll = (llabs(stats[i]-stats[i+8])*1000)/up;
14780f66f451Sopenharmony_ci            pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
14790f66f451Sopenharmony_ci          }
14800f66f451Sopenharmony_ci          lines = header_line(lines, 0);
14810f66f451Sopenharmony_ci        } else {
14820f66f451Sopenharmony_ci          struct ofields *field;
14830f66f451Sopenharmony_ci          struct procpid tb;
14840f66f451Sopenharmony_ci
14850f66f451Sopenharmony_ci          memset(&tb, 0, sizeof(struct procpid));
14860f66f451Sopenharmony_ci          pos = stpcpy(toybuf, "Totals:");
14870f66f451Sopenharmony_ci          for (field = TT.fields; field; field = field->next) {
14880f66f451Sopenharmony_ci            long long ll, bits = 0;
14890f66f451Sopenharmony_ci            int slot = typos[field->which].slot&(XX-1);
14900f66f451Sopenharmony_ci
14910f66f451Sopenharmony_ci            if (field->which<PS_C || field->which>PS_DIO) continue;
14920f66f451Sopenharmony_ci            ll = 1LL<<field->which;
14930f66f451Sopenharmony_ci            if (bits&ll) continue;
14940f66f451Sopenharmony_ci            bits |= ll;
14950f66f451Sopenharmony_ci            for (i=0; i<mix.count; i++)
14960f66f451Sopenharmony_ci              tb.slot[slot] += mix.tb[i]->slot[slot];
14970f66f451Sopenharmony_ci            pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
14980f66f451Sopenharmony_ci              " %s: %*s,", typos[field->which].name,
14990f66f451Sopenharmony_ci              field->len, string_field(&tb, field));
15000f66f451Sopenharmony_ci          }
15010f66f451Sopenharmony_ci          *--pos = 0;
15020f66f451Sopenharmony_ci          lines = header_line(lines, 0);
15030f66f451Sopenharmony_ci        }
15040f66f451Sopenharmony_ci
15050f66f451Sopenharmony_ci        get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
15060f66f451Sopenharmony_ci        for (i = 0, is = ' '; *pos; pos++) {
15070f66f451Sopenharmony_ci          was = is;
15080f66f451Sopenharmony_ci          is = *pos;
15090f66f451Sopenharmony_ci          if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
15100f66f451Sopenharmony_ci            pos[-1] = '[';
15110f66f451Sopenharmony_ci          if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
15120f66f451Sopenharmony_ci        }
15130f66f451Sopenharmony_ci        if (FLAG(b)) while (isspace(*(pos-1))) --pos;
15140f66f451Sopenharmony_ci        *pos = 0;
15150f66f451Sopenharmony_ci        lines = header_line(lines, 1);
15160f66f451Sopenharmony_ci      }
15170f66f451Sopenharmony_ci      if (!recalc && !FLAG(b))
15180f66f451Sopenharmony_ci        printf("\033[%dH\033[J", 1+TT.height-lines);
15190f66f451Sopenharmony_ci      recalc = 1;
15200f66f451Sopenharmony_ci
15210f66f451Sopenharmony_ci      for (i = 0; i<lines && i+topoff<mix.count; i++) {
15220f66f451Sopenharmony_ci        // Running processes are shown in bold.
15230f66f451Sopenharmony_ci        int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R';
15240f66f451Sopenharmony_ci
15250f66f451Sopenharmony_ci        if (!FLAG(b) && i) putchar('\n');
15260f66f451Sopenharmony_ci        if (bold) printf("\033[1m");
15270f66f451Sopenharmony_ci        show_ps(mix.tb[i+topoff]);
15280f66f451Sopenharmony_ci        if (bold) printf("\033[m");
15290f66f451Sopenharmony_ci      }
15300f66f451Sopenharmony_ci
15310f66f451Sopenharmony_ci      if (TT.top.n && !--TT.top.n) {
15320f66f451Sopenharmony_ci        done++;
15330f66f451Sopenharmony_ci        break;
15340f66f451Sopenharmony_ci      }
15350f66f451Sopenharmony_ci
15360f66f451Sopenharmony_ci      now = millitime();
15370f66f451Sopenharmony_ci      if (timeout<=now) timeout = new.whence+TT.top.d;
15380f66f451Sopenharmony_ci      if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
15390f66f451Sopenharmony_ci
15400f66f451Sopenharmony_ci      // In batch mode, we ignore the keyboard.
15410f66f451Sopenharmony_ci      if (FLAG(b)) {
15420f66f451Sopenharmony_ci        msleep(timeout-now);
15430f66f451Sopenharmony_ci        // Make an obvious gap between datasets.
15440f66f451Sopenharmony_ci        xputs("\n\n");
15450f66f451Sopenharmony_ci        break;
15460f66f451Sopenharmony_ci      } else fflush(stdout);
15470f66f451Sopenharmony_ci
15480f66f451Sopenharmony_ci      i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
15490f66f451Sopenharmony_ci      if (i==-1 || i==3 || toupper(i)=='Q') {
15500f66f451Sopenharmony_ci        done++;
15510f66f451Sopenharmony_ci        break;
15520f66f451Sopenharmony_ci      }
15530f66f451Sopenharmony_ci      if (i==-2) break;
15540f66f451Sopenharmony_ci      if (i==-3) continue;
15550f66f451Sopenharmony_ci
15560f66f451Sopenharmony_ci      // Flush unknown escape sequences.
15570f66f451Sopenharmony_ci      if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
15580f66f451Sopenharmony_ci      else if (i=='\r' || i==' ') {
15590f66f451Sopenharmony_ci        timeout = 0;
15600f66f451Sopenharmony_ci        break;
15610f66f451Sopenharmony_ci      } else if (toupper(i)=='R')
15620f66f451Sopenharmony_ci        ((struct ofields *)TT.kfields)->reverse *= -1;
15630f66f451Sopenharmony_ci      else {
15640f66f451Sopenharmony_ci        i -= 256;
15650f66f451Sopenharmony_ci        if (i == KEY_LEFT) setsort(TT.sortpos-1);
15660f66f451Sopenharmony_ci        else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
15670f66f451Sopenharmony_ci        // KEY_UP is 0, so at end of strchr
15680f66f451Sopenharmony_ci        else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
15690f66f451Sopenharmony_ci          recalc = 0;
15700f66f451Sopenharmony_ci
15710f66f451Sopenharmony_ci          if (i == KEY_UP) topoff--;
15720f66f451Sopenharmony_ci          else if (i == KEY_DOWN) topoff++;
15730f66f451Sopenharmony_ci          else if (i == KEY_PGDN) topoff += lines;
15740f66f451Sopenharmony_ci          else if (i == KEY_PGUP) topoff -= lines;
15750f66f451Sopenharmony_ci          if (topoff<0) topoff = 0;
15760f66f451Sopenharmony_ci          if (topoff>mix.count) topoff = mix.count;
15770f66f451Sopenharmony_ci        }
15780f66f451Sopenharmony_ci      }
15790f66f451Sopenharmony_ci      continue;
15800f66f451Sopenharmony_ci    }
15810f66f451Sopenharmony_ci
15820f66f451Sopenharmony_ci    free(mix.tb);
15830f66f451Sopenharmony_ci    for (i=0; i<plold->count; i++) free(plold->tb[i]);
15840f66f451Sopenharmony_ci    free(plold->tb);
15850f66f451Sopenharmony_ci  } while (!done);
15860f66f451Sopenharmony_ci
15870f66f451Sopenharmony_ci  if (!FLAG(b)) tty_reset();
15880f66f451Sopenharmony_ci}
15890f66f451Sopenharmony_ci
15900f66f451Sopenharmony_cistatic void top_setup(char *defo, char *defk)
15910f66f451Sopenharmony_ci{
15920f66f451Sopenharmony_ci  TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
15930f66f451Sopenharmony_ci  TT.tty = tty_fd() != -1;
15940f66f451Sopenharmony_ci
15950f66f451Sopenharmony_ci  // Are we doing "batch" output or interactive?
15960f66f451Sopenharmony_ci  if (FLAG(b)) TT.width = TT.height = 99999;
15970f66f451Sopenharmony_ci  else {
15980f66f451Sopenharmony_ci    // Grab starting time, make terminal raw, switch off cursor,
15990f66f451Sopenharmony_ci    // set signal handler to put terminal/cursor back to normal at exit.
16000f66f451Sopenharmony_ci    TT.time = millitime();
16010f66f451Sopenharmony_ci    start_redraw(&TT.width, &TT.height);
16020f66f451Sopenharmony_ci  }
16030f66f451Sopenharmony_ci
16040f66f451Sopenharmony_ci  comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
16050f66f451Sopenharmony_ci  comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
16060f66f451Sopenharmony_ci  TT.match_process = shared_match_process;
16070f66f451Sopenharmony_ci
16080f66f451Sopenharmony_ci  default_ko(defo, &TT.fields, "bad -o", TT.top.o);
16090f66f451Sopenharmony_ci  dlist_terminate(TT.fields);
16100f66f451Sopenharmony_ci
16110f66f451Sopenharmony_ci  // First (dummy) sort field is overwritten by setsort()
16120f66f451Sopenharmony_ci  default_ko("-S", &TT.kfields, 0, 0);
16130f66f451Sopenharmony_ci  default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
16140f66f451Sopenharmony_ci  dlist_terminate(TT.kfields);
16150f66f451Sopenharmony_ci  setsort(TT.top.s-1);
16160f66f451Sopenharmony_ci}
16170f66f451Sopenharmony_ci
16180f66f451Sopenharmony_ci
16190f66f451Sopenharmony_civoid top_main(void)
16200f66f451Sopenharmony_ci{
16210f66f451Sopenharmony_ci  link_shell_cmd_task();
16220f66f451Sopenharmony_ci}
16230f66f451Sopenharmony_ci
16240f66f451Sopenharmony_ci#define CLEANUP_top
16250f66f451Sopenharmony_ci#define FOR_iotop
16260f66f451Sopenharmony_ci#include "generated/flags.h"
16270f66f451Sopenharmony_ci
16280f66f451Sopenharmony_ci// Compare old and new proces lists to measure changes
16290f66f451Sopenharmony_cistatic int iotop_filter(long long *oslot, long long *nslot, int milis)
16300f66f451Sopenharmony_ci{
16310f66f451Sopenharmony_ci  // Current I/O, or accumulated since process start?
16320f66f451Sopenharmony_ci  if (!FLAG(a)) merge_deltas(oslot, nslot, milis);
16330f66f451Sopenharmony_ci  else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
16340f66f451Sopenharmony_ci
16350f66f451Sopenharmony_ci  return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)];
16360f66f451Sopenharmony_ci}
16370f66f451Sopenharmony_ci
16380f66f451Sopenharmony_civoid iotop_main(void)
16390f66f451Sopenharmony_ci{
16400f66f451Sopenharmony_ci  char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A);
16410f66f451Sopenharmony_ci
16420f66f451Sopenharmony_ci  if (FLAG(K)) TT.forcek++;
16430f66f451Sopenharmony_ci
16440f66f451Sopenharmony_ci  top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
16450f66f451Sopenharmony_ci    s2 = xmprintf("-%sIO,-ETIME,-PID",d));
16460f66f451Sopenharmony_ci  free(s1);
16470f66f451Sopenharmony_ci  free(s2);
16480f66f451Sopenharmony_ci  top_common(iotop_filter);
16490f66f451Sopenharmony_ci}
16500f66f451Sopenharmony_ci
16510f66f451Sopenharmony_ci// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
16520f66f451Sopenharmony_ci// context, so force pgrep's flags on even when building pkill standalone.
16530f66f451Sopenharmony_ci// (All the pgrep/pkill functions drop out when building ps standalone.)
16540f66f451Sopenharmony_ci#define FORCE_FLAGS
16550f66f451Sopenharmony_ci#define CLEANUP_iotop
16560f66f451Sopenharmony_ci#define FOR_pgrep
16570f66f451Sopenharmony_ci#include "generated/flags.h"
16580f66f451Sopenharmony_ci
16590f66f451Sopenharmony_cistruct regex_list {
16600f66f451Sopenharmony_ci  struct regex_list *next;
16610f66f451Sopenharmony_ci  regex_t reg;
16620f66f451Sopenharmony_ci};
16630f66f451Sopenharmony_ci
16640f66f451Sopenharmony_cistatic void do_pgk(struct procpid *tb)
16650f66f451Sopenharmony_ci{
16660f66f451Sopenharmony_ci  if (TT.pgrep.signal) {
16670f66f451Sopenharmony_ci    if (kill(*tb->slot, TT.pgrep.signal)) {
16680f66f451Sopenharmony_ci      char *s = num_to_sig(TT.pgrep.signal);
16690f66f451Sopenharmony_ci
16700f66f451Sopenharmony_ci      if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
16710f66f451Sopenharmony_ci      perror_msg("%s->%lld", s, *tb->slot);
16720f66f451Sopenharmony_ci    }
16730f66f451Sopenharmony_ci  }
16740f66f451Sopenharmony_ci  if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) {
16750f66f451Sopenharmony_ci    printf("%lld", *tb->slot);
16760f66f451Sopenharmony_ci    if (FLAG(l))
16770f66f451Sopenharmony_ci      printf(" %s", tb->str+tb->offset[4]*!!FLAG(f));
16780f66f451Sopenharmony_ci
16790f66f451Sopenharmony_ci    printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
16800f66f451Sopenharmony_ci  }
16810f66f451Sopenharmony_ci}
16820f66f451Sopenharmony_ci
16830f66f451Sopenharmony_cistatic void match_pgrep(void *p)
16840f66f451Sopenharmony_ci{
16850f66f451Sopenharmony_ci  struct procpid *tb = p;
16860f66f451Sopenharmony_ci  regmatch_t match;
16870f66f451Sopenharmony_ci  struct regex_list *reg;
16880f66f451Sopenharmony_ci  char *name = tb->str+tb->offset[4]*!!FLAG(f);
16890f66f451Sopenharmony_ci
16900f66f451Sopenharmony_ci  // Never match ourselves.
16910f66f451Sopenharmony_ci  if (TT.pgrep.self == *tb->slot) return;
16920f66f451Sopenharmony_ci
16930f66f451Sopenharmony_ci  if (TT.pgrep.regexes) {
16940f66f451Sopenharmony_ci    for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
16950f66f451Sopenharmony_ci      if (regexec(&reg->reg, name, 1, &match, 0)) continue;
16960f66f451Sopenharmony_ci      if (FLAG(x))
16970f66f451Sopenharmony_ci        if (match.rm_so || match.rm_eo!=strlen(name)) continue;
16980f66f451Sopenharmony_ci      break;
16990f66f451Sopenharmony_ci    }
17000f66f451Sopenharmony_ci    if (!FLAG(v) == !reg) return;
17010f66f451Sopenharmony_ci  }
17020f66f451Sopenharmony_ci
17030f66f451Sopenharmony_ci  // pgrep should return success if there's a match.
17040f66f451Sopenharmony_ci  toys.exitval = 0;
17050f66f451Sopenharmony_ci
17060f66f451Sopenharmony_ci  // Repurpose a field for -c count.
17070f66f451Sopenharmony_ci  TT.sortpos++;
17080f66f451Sopenharmony_ci  if (FLAG(n)||FLAG(o)) {
17090f66f451Sopenharmony_ci    long long ll = tb->slot[SLOT_starttime];
17100f66f451Sopenharmony_ci
17110f66f451Sopenharmony_ci    if (FLAG(o)) ll *= -1;
17120f66f451Sopenharmony_ci    if (TT.time && TT.time>ll) return;
17130f66f451Sopenharmony_ci    TT.time = ll;
17140f66f451Sopenharmony_ci    free(TT.pgrep.snapshot);
17150f66f451Sopenharmony_ci    TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
17160f66f451Sopenharmony_ci  } else do_pgk(tb);
17170f66f451Sopenharmony_ci}
17180f66f451Sopenharmony_ci
17190f66f451Sopenharmony_cistatic int pgrep_match_process(long long *slot)
17200f66f451Sopenharmony_ci{
17210f66f451Sopenharmony_ci  return !FLAG(v) == !!shared_match_process(slot);
17220f66f451Sopenharmony_ci}
17230f66f451Sopenharmony_ci
17240f66f451Sopenharmony_civoid pgrep_main(void)
17250f66f451Sopenharmony_ci{
17260f66f451Sopenharmony_ci  char **arg;
17270f66f451Sopenharmony_ci  struct regex_list *reg;
17280f66f451Sopenharmony_ci
17290f66f451Sopenharmony_ci  TT.pgrep.self = getpid();
17300f66f451Sopenharmony_ci
17310f66f451Sopenharmony_ci  // No signal names start with "L", so no need for "L: " in optstr.
17320f66f451Sopenharmony_ci  if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
17330f66f451Sopenharmony_ci    error_exit("bad -L '%s'", TT.pgrep.L);
17340f66f451Sopenharmony_ci
17350f66f451Sopenharmony_ci  comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
17360f66f451Sopenharmony_ci  comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
17370f66f451Sopenharmony_ci  comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
17380f66f451Sopenharmony_ci  comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
17390f66f451Sopenharmony_ci  comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
17400f66f451Sopenharmony_ci  comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
17410f66f451Sopenharmony_ci  comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
17420f66f451Sopenharmony_ci
17430f66f451Sopenharmony_ci  if ((toys.optflags&(FLAG_x|FLAG_f)) ||
17440f66f451Sopenharmony_ci      !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
17450f66f451Sopenharmony_ci    if (!toys.optc) help_exit("No PATTERN");
17460f66f451Sopenharmony_ci
17470f66f451Sopenharmony_ci  if (FLAG(f)) TT.bits |= _PS_CMDLINE;
17480f66f451Sopenharmony_ci  for (arg = toys.optargs; *arg; arg++) {
17490f66f451Sopenharmony_ci    reg = xmalloc(sizeof(struct regex_list));
17500f66f451Sopenharmony_ci    xregcomp(&reg->reg, *arg, REG_EXTENDED);
17510f66f451Sopenharmony_ci    reg->next = TT.pgrep.regexes;
17520f66f451Sopenharmony_ci    TT.pgrep.regexes = reg;
17530f66f451Sopenharmony_ci  }
17540f66f451Sopenharmony_ci  TT.match_process = pgrep_match_process;
17550f66f451Sopenharmony_ci  TT.show_process = match_pgrep;
17560f66f451Sopenharmony_ci
17570f66f451Sopenharmony_ci  // pgrep should return failure if there are no matches.
17580f66f451Sopenharmony_ci  toys.exitval = 1;
17590f66f451Sopenharmony_ci
17600f66f451Sopenharmony_ci  dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
17610f66f451Sopenharmony_ci  if (FLAG(c)) printf("%d\n", TT.sortpos);
17620f66f451Sopenharmony_ci  if (TT.pgrep.snapshot) {
17630f66f451Sopenharmony_ci    do_pgk(TT.pgrep.snapshot);
17640f66f451Sopenharmony_ci    if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
17650f66f451Sopenharmony_ci  }
17660f66f451Sopenharmony_ci  if (TT.pgrep.d) xputc('\n');
17670f66f451Sopenharmony_ci}
17680f66f451Sopenharmony_ci
17690f66f451Sopenharmony_ci#define CLEANUP_pgrep
17700f66f451Sopenharmony_ci#define FOR_pkill
17710f66f451Sopenharmony_ci#include "generated/flags.h"
17720f66f451Sopenharmony_ci
17730f66f451Sopenharmony_civoid pkill_main(void)
17740f66f451Sopenharmony_ci{
17750f66f451Sopenharmony_ci  char **args = toys.optargs;
17760f66f451Sopenharmony_ci
17770f66f451Sopenharmony_ci  if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
17780f66f451Sopenharmony_ci  if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
17790f66f451Sopenharmony_ci  if (FLAG(V)) TT.tty = 1;
17800f66f451Sopenharmony_ci  pgrep_main();
17810f66f451Sopenharmony_ci}
1782