1 /* ps.c - show process list
3 * Copyright 2015 Rob Landley <rob@landley.net>
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()
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.
18 * Posix defines -o ADDR as "The address of the process" but the process
19 * start address is a constant on any elf system with mmu. The procps ADDR
20 * field always prints "-" with an alignment of 1, which is why it has 11
21 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
22 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
23 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
24 * which changes -l by removing the "F" column and swapping RSS for ADDR,
25 * leaving 9 chars for cmd, so we're using that as our -l output.
27 * Added a bunch of new -o fields posix doesn't mention, and we don't
28 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
29 * output argv[0] unmodified for -o comm or -o args (but procps violates
30 * posix for -o comm anyway, it's stat[2] not argv[0]).
32 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
33 * files (why they're not globally readable when the rest of proc
34 * data is...?) and get a global I/O picture. Normal top is NOT,
35 * even though you can -o AIO there, to give sysadmins the option
36 * to reduce security exposure.)
38 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
39 * TODO: switch -fl to -y
40 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
41 * TODO: iotop: Window size change: respond immediately. Why not padding
42 * at right edge? (Not adjusting to screen size at all? Header wraps?)
43 * TODO: top: thread support and SMP
44 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
46 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))
47 // stayroot because iotop needs root to read other process' proc/$$/io
48 USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
49 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
50 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
51 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
57 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
61 Which processes to show (selections may be comma separated lists):
64 -a Processes with terminals that aren't session leaders
65 -d All processes that aren't session leaders
67 -g Belonging to GROUPs
68 -G Belonging to real GROUPs (before sgid)
70 -P Parent PIDs (--ppid)
72 -t Attached to selected TTYs
75 -U Owned by real USERs (before suid)
79 -k Sort FIELDs in +increasing or -decreasting order (--sort)
80 -M Measure field widths (expanding as necessary)
81 -n Show numeric USER and GROUP
82 -w Wide output (don't truncate at terminal width)
84 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
86 -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
87 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
88 -o Output FIELDs instead of defaults, each with optional :size and =title
89 -O Add FIELDS to defaults
92 Command line -o fields:
94 ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
95 CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
96 COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
98 Process attribute -o FIELDs:
100 ADDR Instruction pointer BIT Is this process 32 or 64 bits
101 CPU Which processor running on ETIME Elapsed time since PID start
102 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
103 GROUP Group name LABEL Security label
104 MAJFL Major page faults MINFL Minor page faults
105 NI Niceness (lower is faster)
106 PCPU Percentage of CPU time used PCY Android scheduling policy
107 PGID Process Group ID
108 PID Process ID PPID Parent Process ID
109 PRI Priority (higher is faster) PSR Processor last executed on
110 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
111 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
112 RUID Real (before suid) user ID RUSER Real (before suid) user name
114 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
115 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
116 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
117 STAT Process state (S) plus:
118 < high priority N low priority L locked memory
119 s session leader + foreground l multithreaded
120 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
121 SZ Memory Size (4k pages needed to completely swap out process)
122 TCNT Thread count TID Thread ID
123 TIME CPU time consumed TTY Controlling terminal
124 UID User id USER User name
125 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
126 WCHAN What are we waiting in kernel for
130 depends on TOP_COMMON
133 usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
135 Show process activity in real time.
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)
143 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
146 depends on TOP_COMMON
151 Rank processes by I/O.
153 -A All I/O, not just disk
154 -a Accumulated I/O (not percentage)
156 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
157 -O Only show processes doing I/O
158 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
159 -s Sort by field number (0-X, default 6)
165 usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
167 -b Batch mode (no tty)
168 -d Delay SECONDS between each cycle (default 3)
169 -n Exit after NUMBER iterations
172 -q Quiet (no header lines)
174 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
175 update, R to reverse sort, Q to exit.
180 depends on PGKILL_COMMON
182 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
184 Search for process(es). PATTERN is an extended regular expression checked
185 against command names.
187 -c Show only count of matches
188 -d Use DELIM instead of newline
189 -L Send SIGNAL instead of printing name
195 depends on PGKILL_COMMON
197 usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
199 -l Send SIGNAL (default SIGTERM)
206 usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
208 -f Check full command line for PATTERN
209 -G Match real Group ID(s)
210 -g Match Process Group(s) (0 is current user)
213 -P Match Parent Process ID(s)
214 -s Match Session ID(s) (0 for current)
216 -U Match real User ID(s)
217 -u Match effective User ID(s)
219 -x Match whole command (not substring)
261 void *regexes, *snapshot;
268 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
269 struct dirtree *threadparent;
270 unsigned width, height;
272 void *fields, *kfields;
273 long long ticks, bits, time;
274 int kcount, forcek, sortpos;
275 int (*match_process)(long long *slot);
276 void (*show_process)(void *tb);
280 struct strawberry *next, *prev;
281 short which, len, reverse;
286 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
287 * table 1-4) but we shift and repurpose fields, with the result being: */
290 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
291 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
292 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
293 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
294 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
295 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
296 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
297 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
298 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
299 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
300 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
301 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
302 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
303 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
304 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
305 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
306 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
307 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
308 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
309 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
310 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
311 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
312 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
313 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
314 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
315 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
316 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
317 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
318 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
319 SLOT_pcy, /*Android sched policy*/
324 // Data layout in toybuf
326 long long slot[SLOT_count]; // data (see enum above)
327 unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
329 char str[]; // name, tty, command, wchan, attr, cmdline
332 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
333 // 64|slot means compare as string when sorting
336 signed char width, slot;
337 } static const typos[] = TAGGED_ARRAY(PS,
339 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
340 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
341 {"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
342 {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
343 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
344 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
345 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
348 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
349 {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
350 {"ARGS", -27, -6}, {"CMD", -15, -1},
353 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
354 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
355 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
358 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
359 {"TIME+", 9, SLOT_utime},
361 // Percentage displays
362 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
363 {"%CPU", 4, SLOT_utime2},
366 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
367 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
368 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
369 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
372 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
373 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
376 // Return 0 to discard, nonzero to keep
377 static int shared_match_process(long long *slot)
379 struct ptr_len match[] = {
380 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
381 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
382 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
387 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
388 for (i = 0; i < ARRAY_LEN(match); i++) {
389 struct ptr_len *mm = match[i].ptr;
393 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
401 // Return 0 to discard, nonzero to keep
402 static int ps_match_process(long long *slot)
404 int i = shared_match_process(slot);
407 // If we had selections and didn't match them, don't display
410 // Filter implicit categories for other display types
411 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
412 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
413 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
414 && TT.tty!=slot[SLOT_ttynr]) return 0;
419 // Convert field to string representation
420 static char *string_field(struct carveup *tb, struct strawberry *field)
422 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
423 int which = field->which, sl = typos[which].slot;
424 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
426 // numbers, mostly from /proc/$PID/stat
427 if (which <= PS_BIT) {
430 if (which==PS_PRI) ll = 39-ll;
431 if (which==PS_ADDR) fmt = "%llx";
432 else if (which==PS_SZ) ll >>= 12;
433 else if (which==PS_RSS) ll <<= 2;
434 else if (which==PS_VSZ) ll >>= 10;
435 else if (which==PS_PR && ll<-9) fmt="RT";
436 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
437 sprintf(out, fmt, ll);
443 // First string slot has offset 0, others are offset[-slot-2]
444 if (--sl) out += tb->offset[--sl];
445 if (which==PS_ARGS || which==PS_COMM) {
449 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
450 if (out[i] == '/') s = out+i+1;
453 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
456 } else if (which <= PS_RGROUP) {
457 sprintf(out, "%lld", ll);
459 if (which > PS_RUSER) {
460 struct group *gr = bufgetgrgid(ll);
462 if (gr) out = gr->gr_name;
464 struct passwd *pw = bufgetpwuid(ll);
466 if (pw) out = pw->pw_name;
471 } else if (which <= PS_TIME_) {
472 int unit = 60, pad = 2, j = TT.ticks;
475 if (which!=PS_TIME_) unit *= 60*24;
477 // top adjusts slot[SLOT_upticks], we want original meaning.
478 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
481 // Output days-hours:mins:secs, skipping non-required fields with zero
482 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
483 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
484 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
486 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
488 if ((*s = "-::"[j])) s++;
493 if (which==PS_TIME_ && s-out<8)
494 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
496 // Percentage displays
497 } else if (which <= PS__CPU) {
498 ll = slot[sl&63]*1000;
499 if (which==PS__VSZ || which==PS__MEM)
500 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
501 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
503 if (which==PS_C) sl += 5;
504 sprintf(out, "%d", sl/10);
505 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
508 } else if (which <= PS_DIO) {
509 ll = slot[typos[which].slot];
510 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
511 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
512 else human_readable(out, ll, 0);
514 // Posix doesn't specify what flags should say. Man page says
515 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
516 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
517 else if (which==PS_S || which==PS_STAT) {
520 if (which==PS_STAT) {
521 // TODO l = multithreaded
522 if (slot[SLOT_nice]<0) *s++ = '<';
523 else if (slot[SLOT_nice]>0) *s++ = 'N';
524 if (slot[SLOT_sid]==*slot) *s++ = 's';
525 if (slot[SLOT_vmlck]) *s++ = 'L';
526 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
529 } else if (which==PS_STIME) {
530 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
532 // Padding behavior's a bit odd: default field size is just hh:mm.
533 // Increasing stime:size reveals more data at left until full,
534 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
535 // then add :ss on right for :19.
536 strftime(out, 260, "%F %T", localtime(&t));
537 out = out+strlen(out)-3-abs(field->len);
538 if (out<buf) out = buf;
540 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
541 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
546 // Display process data that get_ps() read from /proc, formatting with TT.fields
547 static void show_ps(void *p)
549 struct carveup *tb = p;
550 struct strawberry *field;
551 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
553 // Loop through fields to display
554 for (field = TT.fields; field; field = field->next) {
555 char *out = string_field(tb, field);
557 // Output the field, appropriately padded
559 // Minimum one space between each field
560 if (field != TT.fields) {
565 // Don't truncate number fields, but try to reclaim extra offset from later
566 // fields that can naturally be shorter
567 abslen = abs(field->len);
568 sign = field->len<0 ? -1 : 1;
570 if ((field->which<=PS_BIT || (toys.optflags&FLAG_w)) && olen>abslen) {
571 // overflow but remember by how much
572 extra += olen-abslen;
574 } else if (extra && olen<abslen) {
575 // If later fields have slack space, take back overflow
577 if (olen>extra) olen = extra;
581 if (abslen>width) abslen = width;
584 // If last field is left justified, no trailing spaces.
585 if (!field->next && sign<0) {
590 if (TT.tty) width -= draw_trim(out, pad, len);
591 else width -= printf("%*.*s", pad, len, out);
594 xputc(TT.time ? '\r' : '\n');
597 // dirtree callback: read data about process to display, store, or discard it.
598 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
599 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
600 static int get_ps(struct dirtree *new)
603 char *name; // Path under /proc/$PID directory
604 long long bits; // Only fetch extra data if an -o field is displaying it
606 // sources for carveup->offset[] data
607 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
608 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
611 struct carveup *tb = (void *)toybuf;
612 long long *slot = tb->slot;
613 char *name, *s, *buf = tb->str, *end = 0;
617 // Recurse one level into /proc children, skip non-numeric entries
619 return DIRTREE_RECURSE|DIRTREE_SHUTUP
620 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
622 memset(slot, 0, sizeof(tb->slot));
623 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
624 if (TT.threadparent && TT.threadparent->extra)
625 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
626 fd = dirtree_parentfd(new);
629 sprintf(buf, "%lld/stat", *slot);
630 if (!readfileat(fd, buf, buf, &len)) return 0;
632 // parse oddball fields (name and state). Name can have embedded ')' so match
633 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
634 // All remaining fields should be numeric.
635 if (!(name = strchr(buf, '('))) return 0;
636 for (s = ++name; *s; s++) if (*s == ')') end = s;
637 if (!end || end-name>255) return 0;
639 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
640 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
641 for (j = 1; j<SLOT_count; j++)
642 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
644 // Now we've read the data, move status and name right after slot[] array,
645 // and convert low chars to ? for non-tty display while we're at it.
646 for (i = 0; i<end-name; i++)
647 if ((tb->str[i] = name[i]) < ' ')
648 if (!TT.tty) tb->str[i] = '?';
651 len = sizeof(toybuf)-(buf-toybuf);
653 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
654 // or numeric wchan, and the remaining two are always zero), and vmlck into
655 // 18 (which is "obsolete, always 0" from stat)
656 slot[SLOT_uid] = new->st.st_uid;
657 slot[SLOT_gid] = new->st.st_gid;
659 // TIME and TIME+ use combined value, ksort needs 'em added.
660 slot[SLOT_utime] += slot[SLOT_stime];
661 slot[SLOT_utime2] = slot[SLOT_utime];
663 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
664 // and save ruid, rgid, and vmlck.
665 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
666 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
670 sprintf(buf, "%lld/status", *slot);
671 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
672 s = strafter(buf, "\nUid:");
673 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
674 s = strafter(buf, "\nGid:");
675 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
676 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
677 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
680 // Do we need to read "io"?
681 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
684 sprintf(buf, "%lld/io", *slot);
685 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
686 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
687 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
688 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
689 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
690 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
691 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
694 // We now know enough to skip processes we don't care about.
695 if (TT.match_process && !TT.match_process(slot)) return 0;
697 // /proc data is generated as it's read, so for maximum accuracy on slow
698 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
700 slot[SLOT_uptime] = TT.si.uptime;
701 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
703 // Do we need to read "statm"?
704 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
707 sprintf(buf, "%lld/statm", *slot);
708 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
710 for (s = buf, i=0; i<3; i++)
711 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
715 // Do we need to read "exe"?
716 if (TT.bits&_PS_BIT) {
719 sprintf(buf, "%lld/exe", *slot);
720 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
721 if (buf[4] == 1) slot[SLOT_bits] = 32;
722 else if (buf[4] == 2) slot[SLOT_bits] = 64;
726 // Do we need Android scheduling policy?
727 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
729 // Fetch string data while parentfd still available, appending to buf.
730 // (There's well over 3k of toybuf left. We could dynamically malloc, but
731 // it'd almost never get used, querying length of a proc file is awkward,
732 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
733 slot[SLOT_argv0len] = 0;
734 for (j = 0; j<ARRAY_LEN(fetch); j++) {
735 tb->offset[j] = buf-(tb->str);
736 if (!(TT.bits&fetch[j].bits)) {
741 // Determine remaining space, reserving minimum of 256 bytes/field and
742 // 260 bytes scratch space at the end (for output conversion later).
743 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
744 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
746 // For exe we readlink instead of read contents
748 struct carveup *ptb = 0;
751 // Thread doesn't have exe or argv[0], so use parent's
752 if (TT.threadparent && TT.threadparent->extra)
753 ptb = (void *)TT.threadparent->extra;
755 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
757 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
759 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
760 i = ptb->slot[SLOT_argv0len];
761 s = ptb->str+ptb->offset[4];
762 while (-1!=(k = stridx(s, '/')) && k<i) {
772 // If it's not the TTY field, data we want is in a file.
773 // Last length saved in slot[] is command line (which has embedded NULs)
775 int rdev = slot[SLOT_ttynr];
778 // Call no tty "?" rather than "0:0".
781 // Can we readlink() our way to a name?
782 for (i = 0; i<3; i++) {
783 sprintf(buf, "%lld/fd/%i", *slot, i);
784 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
785 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
789 // Couldn't find it, try all the tty drivers.
791 FILE *fp = fopen("/proc/tty/drivers", "r");
792 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
795 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
796 // TODO: we could parse the minor range too.
797 if (tty_major == maj) {
799 len += sprintf(buf+len, "%d", min);
800 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
808 // Really couldn't find it, so just show major:minor.
809 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
813 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
816 // Data we want is in a file.
817 // Last length saved in slot[] is command line (which has embedded NULs)
821 // When command has no arguments, don't space over the NUL
822 if (readfileat(fd, buf, buf, &len) && len>0) {
824 // Trim trailing whitespace and NUL bytes
826 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
829 // Turn NUL to space, other low ascii to ? (in non-tty mode)
830 // cmdline has a trailing NUL that we don't want to turn to space.
831 for (i=0; i<len-1; i++) {
837 } else if (!TT.tty && c<' ') c = '?';
840 } else *buf = len = 0;
842 // Store end of argv[0] so ARGS and CMDLINE can differ.
843 // We do it for each file string slot but last is cmdline, which sticks.
844 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
847 // Above calculated/retained len, so we don't need to re-strlen.
852 if (TT.show_process && !TT.threadparent) {
858 // If we need to sort the output, add it to the list and return.
859 s = xmalloc(buf-toybuf);
860 new->extra = (long)s;
861 memcpy(s, toybuf, buf-toybuf);
866 static int get_threads(struct dirtree *new)
870 unsigned pid, kcount;
872 if (!new->parent) return get_ps(new);
874 if (!(pid = atol(new->name))) return 0;
876 TT.threadparent = new;
883 // Recurse down into tasks, retaining thread groups.
884 // Disable show_process at least until we can calculate tcount
886 sprintf(toybuf, "/proc/%u/task", pid);
887 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
889 kcount = TT.kcount-kcount+1;
890 tb = (void *)new->extra;
891 tb->slot[SLOT_tcount] = kcount;
893 // Fill out tid and thread count for each entry in group
894 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
895 tb = (void *)dt->extra;
896 tb->slot[SLOT_pid] = pid;
897 tb->slot[SLOT_tcount] = kcount;
901 if (!TT.show_process) return DIRTREE_SAVE;
902 TT.show_process((void *)new->extra);
906 new = dt->child->next;
907 TT.show_process((void *)dt->child->extra);
916 static char *parse_ko(void *data, char *type, int length)
918 struct strawberry *field;
919 char *width, *title, *end, *s;
922 // Get title, length of title, type, end of type, and display width
924 // Chip off =name to display
925 if ((end = strchr(type, '=')) && length>(end-type)) {
927 length -= (end-type)+1;
933 // Chip off :width to display
934 if ((width = strchr(type, ':')) && width<end) {
935 if (!title) length = width-type;
938 // Allocate structure, copy title
939 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
941 memcpy(field->title = field->forever, title, length);
942 field->title[field->len = length] = 0;
946 field->len = strtol(++width, &title, 10);
947 if (!isdigit(*width) || title != end) return title;
953 if (*type == '-') field->reverse = -1;
954 else if (*type != '+') type--;
956 for (i = 0; i<ARRAY_LEN(typos); i++) {
958 for (j = 0; j<2; j++) {
959 if (!j) s = typos[i].name;
960 // posix requires alternate names for some fields
961 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
962 PS_VSZ, PS_USER, 0}, i))) continue;
964 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
966 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
970 if (i==ARRAY_LEN(typos)) return type;
971 if (!field->title) field->title = typos[field->which].name;
972 if (!field->len) field->len = typos[field->which].width;
973 else if (typos[field->which].width<0) field->len *= -1;
974 dlist_add_nomalloc(data, (void *)field);
979 long long get_headers(struct strawberry *fields, char *buf, int blen)
984 for (; fields; fields = fields->next) {
985 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
987 bits |= 1LL<<fields->which;
993 // Parse -p -s -t -u -U -g -G
994 static char *parse_rest(void *data, char *str, int len)
996 struct ptr_len *pl = (struct ptr_len *)data;
1001 // Allocate next chunk of data
1003 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1005 // Parse numerical input
1006 if (isdigit(*str)) {
1007 ll[pl->len] = xstrtol(str, &end, 10);
1008 if (end==(len+str)) num++;
1009 // For pkill, -s 0 represents pkill's session id.
1010 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1013 if (pl==&TT.pp || pl==&TT.ss) {
1014 if (num && ll[pl->len]>0) {
1019 } else if (pl==&TT.tt) {
1020 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1022 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1023 if (strstart(&str, "pts/")) {
1026 } else if (strstart(&str, "tty")) len -= 3;
1028 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1031 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1032 memcpy(end, str, len);
1035 ll[pl->len++] = st.st_rdev;
1039 } else if (len<255) {
1048 memcpy(name, str, len);
1050 if (pl==&TT.gg || pl==&TT.GG) {
1051 struct group *gr = getgrnam(name);
1053 ll[pl->len++] = gr->gr_gid;
1057 } else if (pl==&TT.uu || pl==&TT.UU) {
1058 struct passwd *pw = getpwnam(name);
1060 ll[pl->len++] = pw->pw_uid;
1072 static int ksort(void *aa, void *bb)
1074 struct strawberry *field;
1075 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1078 for (field = TT.kfields; field && !ret; field = field->next) {
1079 slot = typos[field->which].slot;
1081 // Can we do numeric sort?
1083 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1084 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1087 // fallback to string sort
1089 memccpy(toybuf, string_field(ta, field), 0, 2048);
1091 ret = strcmp(toybuf, string_field(tb, field));
1093 ret *= field->reverse;
1099 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1102 struct dirtree *next = dt->next;
1104 if (dt->extra) *(tb++) = (void *)dt->extra;
1105 if (dt->child) tb = collate_leaves(tb, dt->child);
1113 static struct carveup **collate(int count, struct dirtree *dt)
1115 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1117 collate_leaves(tbsort, dt);
1122 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1124 struct arg_list def;
1126 memset(&def, 0, sizeof(struct arg_list));
1128 comma_args(arg ? arg : &def, fields, err, parse_ko);
1131 static void shared_main(void)
1135 TT.ticks = sysconf(_SC_CLK_TCK);
1137 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1139 terminal_size(&TT.width, &TT.height);
1142 // find controlling tty, falling back to /dev/tty if none
1143 for (i = 0; !TT.tty && i<4; i++) {
1147 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1149 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1150 if (i==3) close(fd);
1161 if (toys.optflags&FLAG_w) TT.width = 99999;
1164 // parse command line options other than -o
1165 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1166 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1167 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1168 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1169 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1170 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1171 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1172 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1173 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1174 dlist_terminate(TT.kfields);
1176 // It's undocumented, but traditionally extra arguments are extra -p args
1177 for (arg = toys.optargs; *arg; arg++)
1178 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit_raw(*arg);
1180 // Figure out which fields to display
1181 not_o = "%sTTY,TIME,CMD";
1182 if (toys.optflags&FLAG_f)
1183 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
1184 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1185 else if (toys.optflags&FLAG_l)
1186 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1187 else if (CFG_TOYBOX_ON_ANDROID)
1188 sprintf(not_o = toybuf+128,
1189 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1190 (toys.optflags&FLAG_T) ? "CMD" : "NAME");
1191 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1193 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1194 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1195 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1198 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1199 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1200 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1202 dlist_terminate(TT.fields);
1204 // -f and -n change the meaning of some fields
1205 if (toys.optflags&(FLAG_f|FLAG_n)) {
1206 struct strawberry *ever;
1208 for (ever = TT.fields; ever; ever = ever->next) {
1209 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1210 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1215 // Calculate seen fields bit array, and if we aren't deferring printing
1216 // print headers now (for low memory/nommu systems).
1217 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1218 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1219 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = show_ps;
1220 TT.match_process = ps_match_process;
1221 dt = dirtree_read("/proc",
1222 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1223 ? get_threads : get_ps);
1225 if (toys.optflags&(FLAG_k|FLAG_M)) {
1226 struct carveup **tbsort = collate(TT.kcount, dt);
1228 if (toys.optflags&FLAG_M) {
1229 for (i = 0; i<TT.kcount; i++) {
1230 struct strawberry *field;
1232 for (field = TT.fields; field; field = field->next) {
1233 int len = strlen(string_field(tbsort[i], field));
1235 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1239 // Now that we've recalculated field widths, re-pad headers again
1240 get_headers(TT.fields, toybuf, sizeof(toybuf));
1241 printf("%.*s\n", TT.width, toybuf);
1244 if (toys.optflags&FLAG_k)
1245 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1246 for (i = 0; i<TT.kcount; i++) {
1250 if (CFG_TOYBOX_FREE) free(tbsort);
1253 if (CFG_TOYBOX_FREE) {
1262 llist_traverse(TT.fields, free);
1268 #include "generated/flags.h"
1270 // select which of the -o fields to sort by
1271 static void setsort(int pos)
1273 struct strawberry *field, *going2;
1278 for (field = TT.fields; field; field = field->next) {
1279 if ((TT.sortpos = i++)<pos && field->next) continue;
1280 going2 = TT.kfields;
1281 going2->which = field->which;
1282 going2->len = field->len;
1287 // If we have both, adjust slot[deltas[]] to be relative to previous
1288 // measurement rather than process start. Stomping old.data is fine
1289 // because we free it after displaying.
1290 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1292 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1293 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1296 for (i = 0; i<ARRAY_LEN(deltas); i++)
1297 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1298 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1303 static int header_line(int line, int rev)
1305 if (!line) return 0;
1307 if (toys.optflags&FLAG_b) rev = 0;
1309 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1310 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1311 rev ? "\033[0m" : "");
1316 static long long millitime(void)
1320 clock_gettime(CLOCK_MONOTONIC, &ts);
1321 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1324 static void top_common(
1325 int (*filter)(long long *oslot, long long *nslot, int milis))
1327 long long timeout = 0, now, stats[16];
1329 struct carveup **tb;
1332 } plist[2], *plold, *plnew, old, new, mix;
1333 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1334 "iow", "irq", "sirq", "host"};
1337 int i, lines, topoff = 0, done = 0;
1339 toys.signal = SIGWINCH;
1340 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1342 memset(plist, 0, sizeof(plist));
1343 memset(stats, 0, sizeof(stats));
1348 plold = plist+(tock++&1);
1349 plnew = plist+(tock&1);
1350 plnew->whence = millitime();
1351 dt = dirtree_read("/proc",
1352 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1353 ? get_threads : get_ps);
1354 plnew->tb = collate(plnew->count = TT.kcount, dt);
1357 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1358 long long *st = stats+8*(tock&1);
1360 // user nice system idle iowait irq softirq host
1361 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1362 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1365 // First time, wait a quarter of a second to collect a little delta data.
1371 // Collate old and new into "mix", depends on /proc read in pid sort order
1374 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1377 while (old.count || new.count) {
1378 struct carveup *otb = *old.tb, *ntb = *new.tb;
1380 // If we just have old for this process, it exited. Discard it.
1381 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1388 // If we just have new, use it verbatim
1389 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1392 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1393 mix.tb[mix.count] = otb;
1403 // Don't re-fetch data if it's not time yet, just re-display existing data.
1408 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1409 if (!(toys.optflags&FLAG_b)) {
1410 printf("\033[H\033[J");
1413 terminal_probesize(&TT.width, &TT.height);
1418 if (recalc && !(toys.optflags&FLAG_q)) {
1419 // Display "top" header.
1420 if (*toys.which->name == 't') {
1421 struct strawberry alluc;
1422 long long ll, up = 0;
1426 // Count running, sleeping, stopped, zombie processes.
1428 memset(run, 0, sizeof(run));
1429 for (i = 0; i<mix.count; i++)
1430 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1432 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1433 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1434 lines = header_line(lines, 0);
1436 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1437 for (i=0; i<6; i++) {
1438 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1439 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1440 run[i] = pos ? atol(pos) : 0;
1443 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1444 run[0], run[0]-run[1], run[1], run[2]);
1445 lines = header_line(lines, 0);
1447 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1448 run[4], run[4]-run[5], run[5], run[3]);
1449 lines = header_line(lines, 0);
1453 i = sysconf(_SC_NPROCESSORS_CONF);
1454 pos += sprintf(pos, "%d%%cpu", i*100);
1457 // If a processor goes idle it's powered down and its idle ticks don't
1458 // advance, so calculate idle time as potential time - used.
1459 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1462 ll = stats[3] = stats[11] = 0;
1463 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1464 stats[3] = now - llabs(ll);
1466 for (i = 0; i<8; i++) {
1467 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1468 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1470 lines = header_line(lines, 0);
1472 struct strawberry *fields;
1475 memset(&tb, 0, sizeof(struct carveup));
1476 pos = stpcpy(toybuf, "Totals:");
1477 for (fields = TT.fields; fields; fields = fields->next) {
1478 long long ll, bits = 0;
1479 int slot = typos[fields->which].slot&63;
1481 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1482 ll = 1LL<<fields->which;
1483 if (bits&ll) continue;
1485 for (i=0; i<mix.count; i++)
1486 tb.slot[slot] += mix.tb[i]->slot[slot];
1487 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1488 " %s: %*s,", typos[fields->which].name,
1489 fields->len, string_field(&tb, fields));
1492 lines = header_line(lines, 0);
1495 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1496 for (i = 0, is = ' '; *pos; pos++) {
1499 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1501 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1504 lines = header_line(lines, 1);
1506 if (!recalc && !(toys.optflags&FLAG_b))
1507 printf("\033[%dH\033[J", 1+TT.height-lines);
1510 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1511 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1512 show_ps(mix.tb[i+topoff]);
1515 if (TT.top.n && !--TT.top.n) {
1521 if (timeout<=now) timeout = new.whence+TT.top.d;
1522 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1524 // In batch mode, we ignore the keyboard.
1525 if (toys.optflags&FLAG_b) {
1526 msleep(timeout-now);
1527 // Make an obvious gap between datasets.
1532 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1533 if (i==-1 || i==3 || toupper(i)=='Q') {
1539 // Flush unknown escape sequences.
1540 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1544 } else if (toupper(i)=='R')
1545 ((struct strawberry *)TT.kfields)->reverse *= -1;
1548 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1549 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1550 // KEY_UP is 0, so at end of strchr
1551 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1554 if (i == KEY_UP) topoff--;
1555 else if (i == KEY_DOWN) topoff++;
1556 else if (i == KEY_PGDN) topoff += lines;
1557 else if (i == KEY_PGUP) topoff -= lines;
1558 if (topoff<0) topoff = 0;
1559 if (topoff>mix.count) topoff = mix.count;
1566 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1570 if (!(toys.optflags&FLAG_b)) tty_reset();
1573 static void top_setup(char *defo, char *defk)
1576 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1578 TT.time = millitime();
1579 set_terminal(0, 1, 0);
1580 sigatexit(tty_sigreset);
1581 xsignal(SIGWINCH, generic_signal);
1582 printf("\033[?25l\033[0m");
1586 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1587 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1588 TT.match_process = shared_match_process;
1590 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1591 dlist_terminate(TT.fields);
1593 // First (dummy) sort field is overwritten by setsort()
1594 default_ko("-S", &TT.kfields, 0, 0);
1595 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1596 dlist_terminate(TT.kfields);
1597 setsort(TT.top.s-1);
1602 // usage: [-h HEADER] -o OUTPUT -k SORT
1604 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1605 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1606 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1607 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1609 struct strawberry *fields = TT.fields;
1611 fields = fields->next->next;
1612 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1615 top_common(merge_deltas);
1620 #include "generated/flags.h"
1622 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1624 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1625 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1627 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1630 void iotop_main(void)
1632 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1634 if (toys.optflags&FLAG_K) TT.forcek++;
1636 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1637 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1640 top_common(iotop_filter);
1643 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1644 // context, so force pgrep's flags on even when building pkill standalone.
1645 // (All the pgrep/pkill functions drop out when building ps standalone.)
1647 #define CLEANUP_iotop
1649 #include "generated/flags.h"
1652 struct regex_list *next;
1656 static void do_pgk(struct carveup *tb)
1658 if (TT.pgrep.signal) {
1659 if (kill(*tb->slot, TT.pgrep.signal)) {
1660 char *s = num_to_sig(TT.pgrep.signal);
1662 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1663 perror_msg("%s->%lld", s, *tb->slot);
1666 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1667 printf("%lld", *tb->slot);
1668 if (toys.optflags&FLAG_l)
1669 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1671 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1675 static void match_pgrep(void *p)
1677 struct carveup *tb = p;
1679 struct regex_list *reg;
1680 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1682 // Never match ourselves.
1683 if (TT.pgrep.self == *tb->slot) return;
1685 if (TT.pgrep.regexes) {
1686 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1687 if (regexec(®->reg, name, 1, &match, 0)) continue;
1688 if (toys.optflags&FLAG_x)
1689 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1692 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1695 // pgrep should return success if there's a match.
1698 // Repurpose a field for -c count.
1700 if (toys.optflags&(FLAG_n|FLAG_o)) {
1701 long long ll = tb->slot[SLOT_starttime];
1703 if (toys.optflags&FLAG_o) ll *= -1;
1704 if (TT.time && TT.time>ll) return;
1706 free(TT.pgrep.snapshot);
1707 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1711 static int pgrep_match_process(long long *slot)
1713 int match = shared_match_process(slot);
1715 return (toys.optflags&FLAG_v) ? !match : match;
1718 void pgrep_main(void)
1721 struct regex_list *reg;
1723 TT.pgrep.self = getpid();
1725 // No signal names start with "L", so no need for "L: " parsing.
1726 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1727 error_exit("bad -L '%s'", TT.pgrep.L);
1729 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1730 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1731 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1732 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1733 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1734 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1735 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1737 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1738 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1739 if (!toys.optc) help_exit("No PATTERN");
1741 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1742 for (arg = toys.optargs; *arg; arg++) {
1743 reg = xmalloc(sizeof(struct regex_list));
1744 xregcomp(®->reg, *arg, REG_EXTENDED);
1745 reg->next = TT.pgrep.regexes;
1746 TT.pgrep.regexes = reg;
1748 TT.match_process = pgrep_match_process;
1749 TT.show_process = match_pgrep;
1751 // pgrep should return failure if there are no matches.
1754 dirtree_read("/proc", get_ps);
1755 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1756 if (TT.pgrep.snapshot) {
1757 do_pgk(TT.pgrep.snapshot);
1758 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1760 if (TT.pgrep.d) xputc('\n');
1763 #define CLEANUP_pgrep
1765 #include "generated/flags.h"
1767 void pkill_main(void)
1769 char **args = toys.optargs;
1771 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1772 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1773 if (toys.optflags & FLAG_V) TT.tty = 1;