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