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,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 Command line (argv[] -path) CMD COMM, or ARGS with -f
95 CMDLINE Command line (argv[]) COMM Original command name (stat[2])
96 COMMAND Command name (/proc/$PID/exe) NAME Command name (COMMAND -path)
97 TNAME Thread name (argv[0] of $PID)
99 Process attribute -o FIELDs:
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
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
131 depends on TOP_COMMON
134 usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
136 Show process activity in real time.
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)
144 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
147 depends on TOP_COMMON
152 Rank processes by I/O.
154 -A All I/O, not just disk
155 -a Accumulated I/O (not percentage)
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)
166 usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
168 -b Batch mode (no tty)
169 -d Delay SECONDS between each cycle (default 3)
170 -n Exit after NUMBER iterations
173 -q Quiet (no header lines)
175 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
176 update, R to reverse sort, Q to exit.
181 depends on PGKILL_COMMON
183 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
185 Search for process(es). PATTERN is an extended regular expression checked
186 against command names.
188 -c Show only count of matches
189 -d Use DELIM instead of newline
190 -L Send SIGNAL instead of printing name
196 depends on PGKILL_COMMON
198 usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
200 -l Send SIGNAL (default SIGTERM)
207 usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
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)
214 -P Match Parent Process ID(s)
215 -s Match Session ID(s) (0 for current)
217 -U Match real User ID(s)
218 -u Match effective User ID(s)
220 -x Match whole command (not substring)
262 void *regexes, *snapshot;
269 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
270 struct dirtree *threadparent;
271 unsigned width, height;
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);
281 struct strawberry *next, *prev;
282 short which, len, reverse;
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: */
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*/
325 // Data layout in toybuf
327 long long slot[SLOT_count]; // data (see enum above)
328 unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
330 char str[]; // name, tty, command, wchan, attr, cmdline
333 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
334 // 64|slot means compare as string when sorting
337 signed char width, slot;
338 } static const typos[] = TAGGED_ARRAY(PS,
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", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
343 {"VSZ", 6, 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},
349 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -15, -1},
350 {"NAME", -15, -5}, {"TNAME", -27, -7}, {"COMMAND", -27, -5},
351 {"CMDLINE", -27, -6}, {"ARGS", -27, -6}, {"CMD", -27, -1},
354 {"UID", 5, SLOT_uid}, {"USER", -8, 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},
359 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
360 {"TIME+", 9, SLOT_utime},
362 // Percentage displays
363 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
364 {"%CPU", 4, SLOT_utime2},
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},
373 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
374 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
377 // Return 0 to discard, nonzero to keep
378 static int shared_match_process(long long *slot)
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}
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;
394 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
402 // Return 0 to discard, nonzero to keep
403 static int ps_match_process(long long *slot)
405 int i = shared_match_process(slot);
408 // If we had selections and didn't match them, don't display
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;
420 // Convert field to string representation
421 static char *string_field(struct carveup *tb, struct strawberry *field)
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;
427 // numbers, mostly from /proc/$PID/stat
428 if (which <= PS_BIT) {
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);
444 // First string slot has offset 0, others are offset[-slot-2]
445 if (--sl) out += tb->offset[--sl];
446 // If TNAME is blank, show ARGS instead
447 if (which==PS_ARGS && !*out)
448 out = tb->str+tb->offset[-2-typos[which = PS_ARGS].slot];
449 if (which==PS_ARGS || which==PS_NAME) {
453 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
454 if (out[i] == '/') s = out+i+1;
457 if (which != field->which) {
458 j = slot[SLOT_argv0len]-i;
459 if (j > 259) j = 259;
460 memcpy(buf, out+i, j);
465 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
468 } else if (which <= PS_RGROUP) {
469 sprintf(out, "%lld", ll);
471 if (which > PS_RUSER) {
472 struct group *gr = bufgetgrgid(ll);
474 if (gr) out = gr->gr_name;
476 struct passwd *pw = bufgetpwuid(ll);
478 if (pw) out = pw->pw_name;
483 } else if (which <= PS_TIME_) {
484 int unit = 60, pad = 2, j = TT.ticks;
487 if (which!=PS_TIME_) unit *= 60*24;
489 // top adjusts slot[SLOT_upticks], we want original meaning.
490 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
493 // Output days-hours:mins:secs, skipping non-required fields with zero
494 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
495 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
496 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
498 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
500 if ((*s = "-::"[j])) s++;
505 if (which==PS_TIME_ && s-out<8)
506 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
508 // Percentage displays
509 } else if (which <= PS__CPU) {
510 ll = slot[sl&63]*1000;
511 if (which==PS__VSZ || which==PS__MEM)
512 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
513 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
515 if (which==PS_C) sl += 5;
516 sprintf(out, "%d", sl/10);
517 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
520 } else if (which <= PS_DIO) {
521 ll = slot[typos[which].slot];
522 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
523 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
524 else human_readable(out, ll, 0);
526 // Posix doesn't specify what flags should say. Man page says
527 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
528 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
529 else if (which==PS_S || which==PS_STAT) {
532 if (which==PS_STAT) {
533 // TODO l = multithreaded
534 if (slot[SLOT_nice]<0) *s++ = '<';
535 else if (slot[SLOT_nice]>0) *s++ = 'N';
536 if (slot[SLOT_sid]==*slot) *s++ = 's';
537 if (slot[SLOT_vmlck]) *s++ = 'L';
538 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
541 } else if (which==PS_STIME) {
542 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
544 // Padding behavior's a bit odd: default field size is just hh:mm.
545 // Increasing stime:size reveals more data at left until full,
546 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
547 // then add :ss on right for :19.
548 strftime(out, 260, "%F %T", localtime(&t));
549 out = out+strlen(out)-3-abs(field->len);
550 if (out<buf) out = buf;
552 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
553 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
558 // Display process data that get_ps() read from /proc, formatting with TT.fields
559 static void show_ps(struct carveup *tb)
561 struct strawberry *field;
562 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
564 // Loop through fields to display
565 for (field = TT.fields; field; field = field->next) {
566 char *out = string_field(tb, field);
568 // Output the field, appropriately padded
570 // Minimum one space between each field
571 if (field != TT.fields) {
576 // Don't truncate number fields, but try to reclaim extra offset from later
577 // fields that can naturally be shorter
578 abslen = abs(field->len);
579 sign = field->len<0 ? -1 : 1;
580 if (field->which<=PS_BIT || extra) olen = strlen(out);
581 if (field->which<=PS_BIT && olen>abslen) {
582 // overflow but remember by how much
583 extra += olen-abslen;
585 } else if (extra && olen<abslen) {
586 // If later fields have slack space, take back overflow
588 if (olen>extra) olen = extra;
592 if (abslen>width) abslen = width;
595 // If last field is left justified, no trailing spaces.
596 if (!field->next && sign<0) pad = 0;
598 if (TT.tty) width -= draw_trim(out, pad, len);
599 else width -= printf("%*.*s", pad, len, out);
602 xputc(TT.time ? '\r' : '\n');
605 // dirtree callback: read data about process to display, store, or discard it.
606 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
607 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
608 static int get_ps(struct dirtree *new)
611 char *name; // Path under /proc/$PID directory
612 long long bits; // Only fetch extra data if an -o field is displaying it
614 // sources for carveup->offset[] data
615 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
616 {"exe", _PS_COMMAND|_PS_NAME}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_TNAME},
619 struct carveup *tb = (void *)toybuf;
620 long long *slot = tb->slot;
621 char *name, *s, *buf = tb->str, *end = 0;
625 // Recurse one level into /proc children, skip non-numeric entries
627 return DIRTREE_RECURSE|DIRTREE_SHUTUP
628 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
630 memset(slot, 0, sizeof(tb->slot));
631 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
632 if (TT.threadparent && TT.threadparent->extra)
633 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
634 fd = dirtree_parentfd(new);
637 sprintf(buf, "%lld/stat", *slot);
638 if (!readfileat(fd, buf, buf, &len)) return 0;
640 // parse oddball fields (name and state). Name can have embedded ')' so match
641 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
642 // All remaining fields should be numeric.
643 if (!(name = strchr(buf, '('))) return 0;
644 for (s = ++name; *s; s++) if (*s == ')') end = s;
645 if (!end || end-name>255) return 0;
647 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
648 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
649 for (j = 1; j<SLOT_count; j++)
650 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
652 // Now we've read the data, move status and name right after slot[] array,
653 // and convert low chars to ? for non-tty display while we're at it.
654 for (i = 0; i<end-name; i++)
655 if ((tb->str[i] = name[i]) < ' ')
656 if (!TT.tty) tb->str[i] = '?';
659 len = sizeof(toybuf)-(buf-toybuf);
661 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
662 // or numeric wchan, and the remaining two are always zero), and vmlck into
663 // 18 (which is "obsolete, always 0" from stat)
664 slot[SLOT_uid] = new->st.st_uid;
665 slot[SLOT_gid] = new->st.st_gid;
667 // TIME and TIME+ use combined value, ksort needs 'em added.
668 slot[SLOT_utime] += slot[SLOT_stime];
669 slot[SLOT_utime2] = slot[SLOT_utime];
671 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
672 // and save ruid, rgid, and vmlck.
673 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
674 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
678 sprintf(buf, "%lld/status", *slot);
679 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
680 s = strafter(buf, "\nUid:");
681 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
682 s = strafter(buf, "\nGid:");
683 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
684 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
685 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
688 // Do we need to read "io"?
689 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
692 sprintf(buf, "%lld/io", *slot);
693 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
694 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
695 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
696 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
697 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
698 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
699 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
702 // We now know enough to skip processes we don't care about.
703 if (TT.match_process && !TT.match_process(slot)) return 0;
705 // /proc data is generated as it's read, so for maximum accuracy on slow
706 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
708 slot[SLOT_uptime] = TT.si.uptime;
709 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
711 // Do we need to read "statm"?
712 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
715 sprintf(buf, "%lld/statm", *slot);
716 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
718 for (s = buf, i=0; i<3; i++)
719 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
723 // Do we need to read "exe"?
724 if (TT.bits&_PS_BIT) {
727 sprintf(buf, "%lld/exe", *slot);
728 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
729 if (buf[4] == 1) slot[SLOT_bits] = 32;
730 else if (buf[4] == 2) slot[SLOT_bits] = 64;
734 // Do we need Android scheduling policy?
735 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
737 // Fetch string data while parentfd still available, appending to buf.
738 // (There's well over 3k of toybuf left. We could dynamically malloc, but
739 // it'd almost never get used, querying length of a proc file is awkward,
740 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
741 slot[SLOT_argv0len] = 0;
742 for (j = 0; j<ARRAY_LEN(fetch); j++) {
743 tb->offset[j] = buf-(tb->str);
744 if (!(TT.bits&fetch[j].bits)) {
749 // Determine remaining space, reserving minimum of 256 bytes/field and
750 // 260 bytes scratch space at the end (for output conversion later).
751 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
752 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
754 // For exe we readlink instead of read contents
756 struct carveup *ptb = 0;
759 // Thread doesn't have exe or argv[0], so use parent's
760 if (TT.threadparent && TT.threadparent->extra)
761 ptb = (void *)TT.threadparent->extra;
764 if ((len = readlinkat(fd, buf, buf, len))<1) len = 0;
766 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
768 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
769 i = ptb->slot[SLOT_argv0len];
770 s = ptb->str+ptb->offset[4];
771 while (-1!=(k = stridx(s, '/')) && k<i) {
781 // If it's not the TTY field, data we want is in a file.
782 // Last length saved in slot[] is command line (which has embedded NULs)
784 int rdev = slot[SLOT_ttynr];
787 // Call no tty "?" rather than "0:0".
790 // Can we readlink() our way to a name?
791 for (i = 0; i<3; i++) {
792 sprintf(buf, "%lld/fd/%i", *slot, i);
793 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
794 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
801 // Couldn't find it, try all the tty drivers.
803 FILE *fp = fopen("/proc/tty/drivers", "r");
804 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
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) {
811 len += sprintf(buf+len, "%d", min);
812 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
820 // Really couldn't find it, so just show major:minor.
821 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
825 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
828 // Data we want is in a file.
829 // Last length saved in slot[] is command line (which has embedded NULs)
832 // When command has no arguments, don't space over the NUL
833 if (readfileat(fd, buf, buf, &len) && len>0) {
836 // Trim trailing whitespace and NUL bytes
838 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
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++) {
849 } else if (!TT.tty && c<' ') c = '?';
852 // Store end of argv[0] so ARGS and CMDLINE can differ.
853 // We do it for each file string slot but last is cmdline, which sticks.
854 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
855 } else *buf = len = 0;
858 // Above calculated/retained len, so we don't need to re-strlen.
863 if (TT.show_process && !TT.threadparent) {
869 // If we need to sort the output, add it to the list and return.
870 s = xmalloc(buf-toybuf);
871 new->extra = (long)s;
872 memcpy(s, toybuf, buf-toybuf);
877 static int get_threads(struct dirtree *new)
881 unsigned pid, kcount;
883 if (!new->parent) return get_ps(new);
885 if (!(pid = atol(new->name))) return 0;
887 TT.threadparent = new;
894 // Recurse down into tasks, retaining thread groups.
895 // Disable show_process at least until we can calculate tcount
897 sprintf(toybuf, "/proc/%u/task", pid);
898 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
900 kcount = TT.kcount-kcount+1;
901 tb = (void *)new->extra;
902 tb->slot[SLOT_tcount] = kcount;
904 // Fill out tid and thread count for each entry in group
905 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
906 tb = (void *)dt->extra;
907 tb->slot[SLOT_pid] = pid;
908 tb->slot[SLOT_tcount] = kcount;
912 if (!TT.show_process) return DIRTREE_SAVE;
913 TT.show_process((void *)new->extra);
917 new = dt->child->next;
918 TT.show_process((void *)dt->child->extra);
927 static char *parse_ko(void *data, char *type, int length)
929 struct strawberry *field;
930 char *width, *title, *end, *s;
933 // Get title, length of title, type, end of type, and display width
935 // Chip off =name to display
936 if ((end = strchr(type, '=')) && length>(end-type)) {
938 length -= (end-type)+1;
944 // Chip off :width to display
945 if ((width = strchr(type, ':')) && width<end) {
946 if (!title) length = width-type;
949 // Allocate structure, copy title
950 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
952 memcpy(field->title = field->forever, title, length);
953 field->title[field->len = length] = 0;
957 field->len = strtol(++width, &title, 10);
958 if (!isdigit(*width) || title != end) return title;
964 if (*type == '-') field->reverse = -1;
965 else if (*type != '+') type--;
967 for (i = 0; i<ARRAY_LEN(typos); i++) {
969 for (j = 0; j<2; j++) {
970 if (!j) s = typos[i].name;
971 // posix requires alternate names for some fields
972 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
973 PS_VSZ, PS_USER, 0}, i))) continue;
975 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
977 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
981 if (i==ARRAY_LEN(typos)) return type;
982 if (!field->title) field->title = typos[field->which].name;
983 if (!field->len) field->len = typos[field->which].width;
984 else if (typos[field->which].width<0) field->len *= -1;
985 dlist_add_nomalloc(data, (void *)field);
990 long long get_headers(struct strawberry *fields, char *buf, int blen)
995 for (; fields; fields = fields->next) {
996 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
998 bits |= 1LL<<fields->which;
1004 // Parse -p -s -t -u -U -g -G
1005 static char *parse_rest(void *data, char *str, int len)
1007 struct ptr_len *pl = (struct ptr_len *)data;
1012 // Allocate next chunk of data
1014 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1016 // Parse numerical input
1017 if (isdigit(*str)) {
1018 ll[pl->len] = xstrtol(str, &end, 10);
1019 if (end==(len+str)) num++;
1020 // For pkill, -s 0 represents pkill's session id.
1021 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1024 if (pl==&TT.pp || pl==&TT.ss) {
1025 if (num && ll[pl->len]>0) {
1030 } else if (pl==&TT.tt) {
1031 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1033 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1034 if (strstart(&str, "pts/")) {
1037 } else if (strstart(&str, "tty")) len -= 3;
1039 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1042 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1043 memcpy(end, str, len);
1046 ll[pl->len++] = st.st_rdev;
1050 } else if (len<255) {
1059 memcpy(name, str, len);
1061 if (pl==&TT.gg || pl==&TT.GG) {
1062 struct group *gr = getgrnam(name);
1064 ll[pl->len++] = gr->gr_gid;
1068 } else if (pl==&TT.uu || pl==&TT.UU) {
1069 struct passwd *pw = getpwnam(name);
1071 ll[pl->len++] = pw->pw_uid;
1083 static int ksort(void *aa, void *bb)
1085 struct strawberry *field;
1086 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1089 for (field = TT.kfields; field && !ret; field = field->next) {
1090 slot = typos[field->which].slot;
1092 // Can we do numeric sort?
1094 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1095 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1098 // fallback to string sort
1100 memccpy(toybuf, string_field(ta, field), 0, 2048);
1102 ret = strcmp(toybuf, string_field(tb, field));
1104 ret *= field->reverse;
1110 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1113 struct dirtree *next = dt->next;
1115 if (dt->extra) *(tb++) = (void *)dt->extra;
1116 if (dt->child) tb = collate_leaves(tb, dt->child);
1124 static struct carveup **collate(int count, struct dirtree *dt)
1126 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1128 collate_leaves(tbsort, dt);
1133 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1135 struct arg_list def;
1137 memset(&def, 0, sizeof(struct arg_list));
1139 comma_args(arg ? arg : &def, fields, err, parse_ko);
1142 static void shared_main(void)
1146 TT.ticks = sysconf(_SC_CLK_TCK);
1148 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1150 terminal_size(&TT.width, &TT.height);
1153 // find controlling tty, falling back to /dev/tty if none
1154 for (i = 0; !TT.tty && i<4; i++) {
1158 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1160 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1161 if (i==3) close(fd);
1171 if (toys.optflags&FLAG_w) TT.width = 99999;
1174 // parse command line options other than -o
1175 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1176 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1177 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1178 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1179 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1180 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1181 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1182 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1183 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1184 dlist_terminate(TT.kfields);
1186 // Figure out which fields to display
1187 not_o = "%sTTY,TIME,CMD";
1188 if (toys.optflags&FLAG_f)
1189 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,CMD",
1190 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1191 else if (toys.optflags&FLAG_l)
1192 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1193 else if (CFG_TOYBOX_ON_ANDROID)
1194 not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1195 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1197 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1198 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1199 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1202 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1203 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1204 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1206 dlist_terminate(TT.fields);
1208 // -f and -n change the meaning of some fields
1209 if (toys.optflags&(FLAG_f|FLAG_n)) {
1210 struct strawberry *ever;
1212 for (ever = TT.fields; ever; ever = ever->next) {
1213 if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1214 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1215 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1220 // Calculate seen fields bit array, and if we aren't deferring printing
1221 // print headers now (for low memory/nommu systems).
1222 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1223 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1224 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1225 TT.match_process = ps_match_process;
1226 dt = dirtree_read("/proc",
1227 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT|_PS_TNAME)))
1228 ? get_threads : get_ps);
1230 if (toys.optflags&(FLAG_k|FLAG_M)) {
1231 struct carveup **tbsort = collate(TT.kcount, dt);
1233 if (toys.optflags&FLAG_M) {
1234 for (i = 0; i<TT.kcount; i++) {
1235 struct strawberry *field;
1237 for (field = TT.fields; field; field = field->next) {
1238 int len = strlen(string_field(tbsort[i], field));
1240 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1244 // Now that we've recalculated field widths, re-pad headers again
1245 get_headers(TT.fields, toybuf, sizeof(toybuf));
1246 printf("%.*s\n", TT.width, toybuf);
1249 if (toys.optflags&FLAG_k)
1250 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1251 for (i = 0; i<TT.kcount; i++) {
1255 if (CFG_TOYBOX_FREE) free(tbsort);
1258 if (CFG_TOYBOX_FREE) {
1267 llist_traverse(TT.fields, free);
1273 #include "generated/flags.h"
1275 // select which of the -o fields to sort by
1276 static void setsort(int pos)
1278 struct strawberry *field, *going2;
1283 for (field = TT.fields; field; field = field->next) {
1284 if ((TT.sortpos = i++)<pos && field->next) continue;
1285 going2 = TT.kfields;
1286 going2->which = field->which;
1287 going2->len = field->len;
1292 // If we have both, adjust slot[deltas[]] to be relative to previous
1293 // measurement rather than process start. Stomping old.data is fine
1294 // because we free it after displaying.
1295 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1297 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1298 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1301 for (i = 0; i<ARRAY_LEN(deltas); i++)
1302 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1303 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1308 static int header_line(int line, int rev)
1310 if (!line) return 0;
1312 if (toys.optflags&FLAG_b) rev = 0;
1314 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1315 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1316 rev ? "\033[0m" : "");
1321 static long long millitime(void)
1325 clock_gettime(CLOCK_MONOTONIC, &ts);
1326 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1329 static void top_common(
1330 int (*filter)(long long *oslot, long long *nslot, int milis))
1332 long long timeout = 0, now, stats[16];
1334 struct carveup **tb;
1337 } plist[2], *plold, *plnew, old, new, mix;
1338 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1339 "iow", "irq", "sirq", "host"};
1342 int i, lines, topoff = 0, done = 0;
1344 toys.signal = SIGWINCH;
1345 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1347 memset(plist, 0, sizeof(plist));
1348 memset(stats, 0, sizeof(stats));
1353 plold = plist+(tock++&1);
1354 plnew = plist+(tock&1);
1355 plnew->whence = millitime();
1356 dt = dirtree_read("/proc",
1357 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1358 ? get_threads : get_ps);
1359 plnew->tb = collate(plnew->count = TT.kcount, dt);
1362 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1363 long long *st = stats+8*(tock&1);
1365 // user nice system idle iowait irq softirq host
1366 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1367 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1370 // First time, wait a quarter of a second to collect a little delta data.
1376 // Collate old and new into "mix", depends on /proc read in pid sort order
1379 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1382 while (old.count || new.count) {
1383 struct carveup *otb = *old.tb, *ntb = *new.tb;
1385 // If we just have old for this process, it exited. Discard it.
1386 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1393 // If we just have new, use it verbatim
1394 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1397 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1398 mix.tb[mix.count] = otb;
1408 // Don't re-fetch data if it's not time yet, just re-display existing data.
1413 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1414 if (!(toys.optflags&FLAG_b)) {
1415 printf("\033[H\033[J");
1418 terminal_probesize(&TT.width, &TT.height);
1423 if (recalc && !(toys.optflags&FLAG_q)) {
1424 // Display "top" header.
1425 if (*toys.which->name == 't') {
1426 struct strawberry alluc;
1427 long long ll, up = 0;
1431 // Count running, sleeping, stopped, zombie processes.
1433 memset(run, 0, sizeof(run));
1434 for (i = 0; i<mix.count; i++)
1435 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1437 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1438 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1439 lines = header_line(lines, 0);
1441 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1442 for (i=0; i<6; i++) {
1443 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1444 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1445 run[i] = pos ? atol(pos) : 0;
1448 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1449 run[0], run[0]-run[1], run[1], run[2]);
1450 lines = header_line(lines, 0);
1452 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1453 run[4], run[4]-run[5], run[5], run[3]);
1454 lines = header_line(lines, 0);
1458 i = sysconf(_SC_NPROCESSORS_CONF);
1459 pos += sprintf(pos, "%d%%cpu", i*100);
1462 // If a processor goes idle it's powered down and its idle ticks don't
1463 // advance, so calculate idle time as potential time - used.
1464 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1467 ll = stats[3] = stats[11] = 0;
1468 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1469 stats[3] = now - llabs(ll);
1471 for (i = 0; i<8; i++) {
1472 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1473 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1475 lines = header_line(lines, 0);
1477 struct strawberry *fields;
1480 memset(&tb, 0, sizeof(struct carveup));
1481 pos = stpcpy(toybuf, "Totals:");
1482 for (fields = TT.fields; fields; fields = fields->next) {
1483 long long ll, bits = 0;
1484 int slot = typos[fields->which].slot&63;
1486 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1487 ll = 1LL<<fields->which;
1488 if (bits&ll) continue;
1490 for (i=0; i<mix.count; i++)
1491 tb.slot[slot] += mix.tb[i]->slot[slot];
1492 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1493 " %s: %*s,", typos[fields->which].name,
1494 fields->len, string_field(&tb, fields));
1497 lines = header_line(lines, 0);
1500 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1501 for (i = 0, is = ' '; *pos; pos++) {
1504 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1506 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1509 lines = header_line(lines, 1);
1511 if (!recalc && !(toys.optflags&FLAG_b))
1512 printf("\033[%dH\033[J", 1+TT.height-lines);
1515 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1516 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1517 show_ps(mix.tb[i+topoff]);
1520 if (TT.top.n && !--TT.top.n) {
1526 if (timeout<=now) timeout = new.whence+TT.top.d;
1527 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1529 // In batch mode, we ignore the keyboard.
1530 if (toys.optflags&FLAG_b) {
1531 msleep(timeout-now);
1532 // Make an obvious gap between datasets.
1537 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1538 if (i==-1 || i==3 || toupper(i)=='Q') {
1544 // Flush unknown escape sequences.
1545 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1549 } else if (toupper(i)=='R')
1550 ((struct strawberry *)TT.kfields)->reverse *= -1;
1553 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1554 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1555 // KEY_UP is 0, so at end of strchr
1556 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1559 if (i == KEY_UP) topoff--;
1560 else if (i == KEY_DOWN) topoff++;
1561 else if (i == KEY_PGDN) topoff += lines;
1562 else if (i == KEY_PGUP) topoff -= lines;
1563 if (topoff<0) topoff = 0;
1564 if (topoff>mix.count) topoff = mix.count;
1571 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1575 if (!(toys.optflags&FLAG_b)) tty_reset();
1578 static void top_setup(char *defo, char *defk)
1581 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1583 TT.time = millitime();
1584 set_terminal(0, 1, 0);
1585 sigatexit(tty_sigreset);
1586 xsignal(SIGWINCH, generic_signal);
1587 printf("\033[?25l\033[0m");
1591 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1592 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1593 TT.match_process = shared_match_process;
1595 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1596 dlist_terminate(TT.fields);
1598 // First (dummy) sort field is overwritten by setsort()
1599 default_ko("-S", &TT.kfields, 0, 0);
1600 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1601 dlist_terminate(TT.kfields);
1602 setsort(TT.top.s-1);
1607 // usage: [-h HEADER] -o OUTPUT -k SORT
1609 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1610 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1611 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1612 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1614 struct strawberry *fields = TT.fields;
1616 fields = fields->next->next;
1617 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1620 top_common(merge_deltas);
1625 #include "generated/flags.h"
1627 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1629 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1630 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1632 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1635 void iotop_main(void)
1637 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1639 if (toys.optflags&FLAG_K) TT.forcek++;
1641 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1642 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1645 top_common(iotop_filter);
1648 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1649 // context, so force pgrep's flags on even when building pkill standalone.
1650 // (All the pgrep/pkill functions drop out when building ps standalone.)
1652 #define CLEANUP_iotop
1654 #include "generated/flags.h"
1657 struct regex_list *next;
1661 static void do_pgk(struct carveup *tb)
1663 if (TT.pgrep.signal) {
1664 if (kill(*tb->slot, TT.pgrep.signal)) {
1665 char *s = num_to_sig(TT.pgrep.signal);
1667 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1668 perror_msg("%s->%lld", s, *tb->slot);
1671 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1672 printf("%lld", *tb->slot);
1673 if (toys.optflags&FLAG_l)
1674 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1676 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1680 static void match_pgrep(struct carveup *tb)
1683 struct regex_list *reg;
1684 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1686 // Never match ourselves.
1687 if (TT.pgrep.self == *tb->slot) return;
1689 if (TT.pgrep.regexes) {
1690 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1691 if (regexec(®->reg, name, 1, &match, 0)) continue;
1692 if (toys.optflags&FLAG_x)
1693 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1696 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1699 // pgrep should return success if there's a match.
1702 // Repurpose a field for -c count.
1704 if (toys.optflags&(FLAG_n|FLAG_o)) {
1705 long long ll = tb->slot[SLOT_starttime];
1707 if (toys.optflags&FLAG_o) ll *= -1;
1708 if (TT.time && TT.time>ll) return;
1710 free(TT.pgrep.snapshot);
1711 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1715 static int pgrep_match_process(long long *slot)
1717 int match = shared_match_process(slot);
1719 return (toys.optflags&FLAG_v) ? !match : match;
1722 void pgrep_main(void)
1725 struct regex_list *reg;
1727 TT.pgrep.self = getpid();
1729 // No signal names start with "L", so no need for "L: " parsing.
1730 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1731 error_exit("bad -L '%s'", TT.pgrep.L);
1733 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1734 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1735 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1736 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1737 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1738 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1739 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1741 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1742 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1743 if (!toys.optc) help_exit("No PATTERN");
1745 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1746 for (arg = toys.optargs; *arg; arg++) {
1747 reg = xmalloc(sizeof(struct regex_list));
1748 xregcomp(®->reg, *arg, REG_EXTENDED);
1749 reg->next = TT.pgrep.regexes;
1750 TT.pgrep.regexes = reg;
1752 TT.match_process = pgrep_match_process;
1753 TT.show_process = (void *)match_pgrep;
1755 // pgrep should return failure if there are no matches.
1758 dirtree_read("/proc", get_ps);
1759 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1760 if (TT.pgrep.snapshot) {
1761 do_pgk(TT.pgrep.snapshot);
1762 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1764 if (TT.pgrep.d) xputc('\n');
1767 #define CLEANUP_pgrep
1769 #include "generated/flags.h"
1771 void pkill_main(void)
1773 char **args = toys.optargs;
1775 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1776 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1777 if (toys.optflags & FLAG_V) TT.tty = 1;