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