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