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) {
601 if (TT.tty) width -= draw_trim(out, pad, len);
602 else width -= printf("%*.*s", pad, len, out);
605 xputc(TT.time ? '\r' : '\n');
608 // dirtree callback: read data about process to display, store, or discard it.
609 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
610 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
611 static int get_ps(struct dirtree *new)
614 char *name; // Path under /proc/$PID directory
615 long long bits; // Only fetch extra data if an -o field is displaying it
617 // sources for carveup->offset[] data
618 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
619 {"exe", _PS_COMMAND|_PS_NAME}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_TNAME},
622 struct carveup *tb = (void *)toybuf;
623 long long *slot = tb->slot;
624 char *name, *s, *buf = tb->str, *end = 0;
628 // Recurse one level into /proc children, skip non-numeric entries
630 return DIRTREE_RECURSE|DIRTREE_SHUTUP
631 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
633 memset(slot, 0, sizeof(tb->slot));
634 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
635 if (TT.threadparent && TT.threadparent->extra)
636 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
637 fd = dirtree_parentfd(new);
640 sprintf(buf, "%lld/stat", *slot);
641 if (!readfileat(fd, buf, buf, &len)) return 0;
643 // parse oddball fields (name and state). Name can have embedded ')' so match
644 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
645 // All remaining fields should be numeric.
646 if (!(name = strchr(buf, '('))) return 0;
647 for (s = ++name; *s; s++) if (*s == ')') end = s;
648 if (!end || end-name>255) return 0;
650 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
651 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
652 for (j = 1; j<SLOT_count; j++)
653 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
655 // Now we've read the data, move status and name right after slot[] array,
656 // and convert low chars to ? for non-tty display while we're at it.
657 for (i = 0; i<end-name; i++)
658 if ((tb->str[i] = name[i]) < ' ')
659 if (!TT.tty) tb->str[i] = '?';
662 len = sizeof(toybuf)-(buf-toybuf);
664 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
665 // or numeric wchan, and the remaining two are always zero), and vmlck into
666 // 18 (which is "obsolete, always 0" from stat)
667 slot[SLOT_uid] = new->st.st_uid;
668 slot[SLOT_gid] = new->st.st_gid;
670 // TIME and TIME+ use combined value, ksort needs 'em added.
671 slot[SLOT_utime] += slot[SLOT_stime];
672 slot[SLOT_utime2] = slot[SLOT_utime];
674 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
675 // and save ruid, rgid, and vmlck.
676 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
677 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
681 sprintf(buf, "%lld/status", *slot);
682 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
683 s = strafter(buf, "\nUid:");
684 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
685 s = strafter(buf, "\nGid:");
686 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
687 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
688 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
691 // Do we need to read "io"?
692 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
695 sprintf(buf, "%lld/io", *slot);
696 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
697 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
698 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
699 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
700 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
701 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
702 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
705 // We now know enough to skip processes we don't care about.
706 if (TT.match_process && !TT.match_process(slot)) return 0;
708 // /proc data is generated as it's read, so for maximum accuracy on slow
709 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
711 slot[SLOT_uptime] = TT.si.uptime;
712 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
714 // Do we need to read "statm"?
715 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
718 sprintf(buf, "%lld/statm", *slot);
719 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
721 for (s = buf, i=0; i<3; i++)
722 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
726 // Do we need to read "exe"?
727 if (TT.bits&_PS_BIT) {
730 sprintf(buf, "%lld/exe", *slot);
731 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
732 if (buf[4] == 1) slot[SLOT_bits] = 32;
733 else if (buf[4] == 2) slot[SLOT_bits] = 64;
737 // Do we need Android scheduling policy?
738 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
740 // Fetch string data while parentfd still available, appending to buf.
741 // (There's well over 3k of toybuf left. We could dynamically malloc, but
742 // it'd almost never get used, querying length of a proc file is awkward,
743 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
744 slot[SLOT_argv0len] = 0;
745 for (j = 0; j<ARRAY_LEN(fetch); j++) {
746 tb->offset[j] = buf-(tb->str);
747 if (!(TT.bits&fetch[j].bits)) {
752 // Determine remaining space, reserving minimum of 256 bytes/field and
753 // 260 bytes scratch space at the end (for output conversion later).
754 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
755 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
757 // For exe we readlink instead of read contents
759 struct carveup *ptb = 0;
762 // Thread doesn't have exe or argv[0], so use parent's
763 if (TT.threadparent && TT.threadparent->extra)
764 ptb = (void *)TT.threadparent->extra;
767 if ((len = readlinkat(fd, buf, buf, len))<1) len = 0;
769 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
771 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
772 i = ptb->slot[SLOT_argv0len];
773 s = ptb->str+ptb->offset[4];
774 while (-1!=(k = stridx(s, '/')) && k<i) {
784 // If it's not the TTY field, data we want is in a file.
785 // Last length saved in slot[] is command line (which has embedded NULs)
787 int rdev = slot[SLOT_ttynr];
790 // Call no tty "?" rather than "0:0".
793 // Can we readlink() our way to a name?
794 for (i = 0; i<3; i++) {
795 sprintf(buf, "%lld/fd/%i", *slot, i);
796 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
797 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
804 // Couldn't find it, try all the tty drivers.
806 FILE *fp = fopen("/proc/tty/drivers", "r");
807 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
810 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
811 // TODO: we could parse the minor range too.
812 if (tty_major == maj) {
814 len += sprintf(buf+len, "%d", min);
815 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
823 // Really couldn't find it, so just show major:minor.
824 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
828 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
831 // Data we want is in a file.
832 // Last length saved in slot[] is command line (which has embedded NULs)
835 // When command has no arguments, don't space over the NUL
836 if (readfileat(fd, buf, buf, &len) && len>0) {
839 // Trim trailing whitespace and NUL bytes
841 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
844 // Turn NUL to space, other low ascii to ? (in non-tty mode)
845 // cmdline has a trailing NUL that we don't want to turn to space.
846 for (i=0; i<len-1; i++) {
852 } else if (!TT.tty && c<' ') c = '?';
855 // Store end of argv[0] so ARGS and CMDLINE can differ.
856 // We do it for each file string slot but last is cmdline, which sticks.
857 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
858 } else *buf = len = 0;
861 // Above calculated/retained len, so we don't need to re-strlen.
866 if (TT.show_process && !TT.threadparent) {
872 // If we need to sort the output, add it to the list and return.
873 s = xmalloc(buf-toybuf);
874 new->extra = (long)s;
875 memcpy(s, toybuf, buf-toybuf);
880 static int get_threads(struct dirtree *new)
884 unsigned pid, kcount;
886 if (!new->parent) return get_ps(new);
888 if (!(pid = atol(new->name))) return 0;
890 TT.threadparent = new;
897 // Recurse down into tasks, retaining thread groups.
898 // Disable show_process at least until we can calculate tcount
900 sprintf(toybuf, "/proc/%u/task", pid);
901 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
903 kcount = TT.kcount-kcount+1;
904 tb = (void *)new->extra;
905 tb->slot[SLOT_tcount] = kcount;
907 // Fill out tid and thread count for each entry in group
908 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
909 tb = (void *)dt->extra;
910 tb->slot[SLOT_pid] = pid;
911 tb->slot[SLOT_tcount] = kcount;
915 if (!TT.show_process) return DIRTREE_SAVE;
916 TT.show_process((void *)new->extra);
920 new = dt->child->next;
921 TT.show_process((void *)dt->child->extra);
930 static char *parse_ko(void *data, char *type, int length)
932 struct strawberry *field;
933 char *width, *title, *end, *s;
936 // Get title, length of title, type, end of type, and display width
938 // Chip off =name to display
939 if ((end = strchr(type, '=')) && length>(end-type)) {
941 length -= (end-type)+1;
947 // Chip off :width to display
948 if ((width = strchr(type, ':')) && width<end) {
949 if (!title) length = width-type;
952 // Allocate structure, copy title
953 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
955 memcpy(field->title = field->forever, title, length);
956 field->title[field->len = length] = 0;
960 field->len = strtol(++width, &title, 10);
961 if (!isdigit(*width) || title != end) return title;
967 if (*type == '-') field->reverse = -1;
968 else if (*type != '+') type--;
970 for (i = 0; i<ARRAY_LEN(typos); i++) {
972 for (j = 0; j<2; j++) {
973 if (!j) s = typos[i].name;
974 // posix requires alternate names for some fields
975 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
976 PS_VSZ, PS_USER, 0}, i))) continue;
978 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
980 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
984 if (i==ARRAY_LEN(typos)) return type;
985 if (!field->title) field->title = typos[field->which].name;
986 if (!field->len) field->len = typos[field->which].width;
987 else if (typos[field->which].width<0) field->len *= -1;
988 dlist_add_nomalloc(data, (void *)field);
993 long long get_headers(struct strawberry *fields, char *buf, int blen)
998 for (; fields; fields = fields->next) {
999 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
1001 bits |= 1LL<<fields->which;
1007 // Parse -p -s -t -u -U -g -G
1008 static char *parse_rest(void *data, char *str, int len)
1010 struct ptr_len *pl = (struct ptr_len *)data;
1015 // Allocate next chunk of data
1017 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1019 // Parse numerical input
1020 if (isdigit(*str)) {
1021 ll[pl->len] = xstrtol(str, &end, 10);
1022 if (end==(len+str)) num++;
1023 // For pkill, -s 0 represents pkill's session id.
1024 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1027 if (pl==&TT.pp || pl==&TT.ss) {
1028 if (num && ll[pl->len]>0) {
1033 } else if (pl==&TT.tt) {
1034 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1036 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1037 if (strstart(&str, "pts/")) {
1040 } else if (strstart(&str, "tty")) len -= 3;
1042 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1045 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1046 memcpy(end, str, len);
1049 ll[pl->len++] = st.st_rdev;
1053 } else if (len<255) {
1062 memcpy(name, str, len);
1064 if (pl==&TT.gg || pl==&TT.GG) {
1065 struct group *gr = getgrnam(name);
1067 ll[pl->len++] = gr->gr_gid;
1071 } else if (pl==&TT.uu || pl==&TT.UU) {
1072 struct passwd *pw = getpwnam(name);
1074 ll[pl->len++] = pw->pw_uid;
1086 static int ksort(void *aa, void *bb)
1088 struct strawberry *field;
1089 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1092 for (field = TT.kfields; field && !ret; field = field->next) {
1093 slot = typos[field->which].slot;
1095 // Can we do numeric sort?
1097 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1098 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1101 // fallback to string sort
1103 memccpy(toybuf, string_field(ta, field), 0, 2048);
1105 ret = strcmp(toybuf, string_field(tb, field));
1107 ret *= field->reverse;
1113 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1116 struct dirtree *next = dt->next;
1118 if (dt->extra) *(tb++) = (void *)dt->extra;
1119 if (dt->child) tb = collate_leaves(tb, dt->child);
1127 static struct carveup **collate(int count, struct dirtree *dt)
1129 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1131 collate_leaves(tbsort, dt);
1136 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1138 struct arg_list def;
1140 memset(&def, 0, sizeof(struct arg_list));
1142 comma_args(arg ? arg : &def, fields, err, parse_ko);
1145 static void shared_main(void)
1149 TT.ticks = sysconf(_SC_CLK_TCK);
1151 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1153 terminal_size(&TT.width, &TT.height);
1156 // find controlling tty, falling back to /dev/tty if none
1157 for (i = 0; !TT.tty && i<4; i++) {
1161 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1163 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1164 if (i==3) close(fd);
1174 if (toys.optflags&FLAG_w) TT.width = 99999;
1177 // parse command line options other than -o
1178 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1179 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1180 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1181 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1182 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1183 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1184 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1185 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1186 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1187 dlist_terminate(TT.kfields);
1189 // Figure out which fields to display
1190 not_o = "%sTTY,TIME,CMD";
1191 if (toys.optflags&FLAG_f)
1192 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,CMD",
1193 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1194 else if (toys.optflags&FLAG_l)
1195 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1196 else if (CFG_TOYBOX_ON_ANDROID)
1197 not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1198 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1200 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1201 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1202 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1205 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1206 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1207 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1209 dlist_terminate(TT.fields);
1211 // -f and -n change the meaning of some fields
1212 if (toys.optflags&(FLAG_f|FLAG_n)) {
1213 struct strawberry *ever;
1215 for (ever = TT.fields; ever; ever = ever->next) {
1216 if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1217 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1218 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1223 // Calculate seen fields bit array, and if we aren't deferring printing
1224 // print headers now (for low memory/nommu systems).
1225 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1226 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1227 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1228 TT.match_process = ps_match_process;
1229 dt = dirtree_read("/proc",
1230 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT|_PS_TNAME)))
1231 ? get_threads : get_ps);
1233 if (toys.optflags&(FLAG_k|FLAG_M)) {
1234 struct carveup **tbsort = collate(TT.kcount, dt);
1236 if (toys.optflags&FLAG_M) {
1237 for (i = 0; i<TT.kcount; i++) {
1238 struct strawberry *field;
1240 for (field = TT.fields; field; field = field->next) {
1241 int len = strlen(string_field(tbsort[i], field));
1243 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1247 // Now that we've recalculated field widths, re-pad headers again
1248 get_headers(TT.fields, toybuf, sizeof(toybuf));
1249 printf("%.*s\n", TT.width, toybuf);
1252 if (toys.optflags&FLAG_k)
1253 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1254 for (i = 0; i<TT.kcount; i++) {
1258 if (CFG_TOYBOX_FREE) free(tbsort);
1261 if (CFG_TOYBOX_FREE) {
1270 llist_traverse(TT.fields, free);
1276 #include "generated/flags.h"
1278 // select which of the -o fields to sort by
1279 static void setsort(int pos)
1281 struct strawberry *field, *going2;
1286 for (field = TT.fields; field; field = field->next) {
1287 if ((TT.sortpos = i++)<pos && field->next) continue;
1288 going2 = TT.kfields;
1289 going2->which = field->which;
1290 going2->len = field->len;
1295 // If we have both, adjust slot[deltas[]] to be relative to previous
1296 // measurement rather than process start. Stomping old.data is fine
1297 // because we free it after displaying.
1298 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1300 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1301 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1304 for (i = 0; i<ARRAY_LEN(deltas); i++)
1305 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1306 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1311 static int header_line(int line, int rev)
1313 if (!line) return 0;
1315 if (toys.optflags&FLAG_b) rev = 0;
1317 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1318 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1319 rev ? "\033[0m" : "");
1324 static long long millitime(void)
1328 clock_gettime(CLOCK_MONOTONIC, &ts);
1329 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1332 static void top_common(
1333 int (*filter)(long long *oslot, long long *nslot, int milis))
1335 long long timeout = 0, now, stats[16];
1337 struct carveup **tb;
1340 } plist[2], *plold, *plnew, old, new, mix;
1341 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1342 "iow", "irq", "sirq", "host"};
1345 int i, lines, topoff = 0, done = 0;
1347 toys.signal = SIGWINCH;
1348 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1350 memset(plist, 0, sizeof(plist));
1351 memset(stats, 0, sizeof(stats));
1356 plold = plist+(tock++&1);
1357 plnew = plist+(tock&1);
1358 plnew->whence = millitime();
1359 dt = dirtree_read("/proc",
1360 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1361 ? get_threads : get_ps);
1362 plnew->tb = collate(plnew->count = TT.kcount, dt);
1365 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1366 long long *st = stats+8*(tock&1);
1368 // user nice system idle iowait irq softirq host
1369 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1370 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1373 // First time, wait a quarter of a second to collect a little delta data.
1379 // Collate old and new into "mix", depends on /proc read in pid sort order
1382 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1385 while (old.count || new.count) {
1386 struct carveup *otb = *old.tb, *ntb = *new.tb;
1388 // If we just have old for this process, it exited. Discard it.
1389 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1396 // If we just have new, use it verbatim
1397 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1400 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1401 mix.tb[mix.count] = otb;
1411 // Don't re-fetch data if it's not time yet, just re-display existing data.
1416 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1417 if (!(toys.optflags&FLAG_b)) {
1418 printf("\033[H\033[J");
1421 terminal_probesize(&TT.width, &TT.height);
1426 if (recalc && !(toys.optflags&FLAG_q)) {
1427 // Display "top" header.
1428 if (*toys.which->name == 't') {
1429 struct strawberry alluc;
1430 long long ll, up = 0;
1434 // Count running, sleeping, stopped, zombie processes.
1436 memset(run, 0, sizeof(run));
1437 for (i = 0; i<mix.count; i++)
1438 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1440 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1441 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1442 lines = header_line(lines, 0);
1444 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1445 for (i=0; i<6; i++) {
1446 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1447 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1448 run[i] = pos ? atol(pos) : 0;
1451 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1452 run[0], run[0]-run[1], run[1], run[2]);
1453 lines = header_line(lines, 0);
1455 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1456 run[4], run[4]-run[5], run[5], run[3]);
1457 lines = header_line(lines, 0);
1461 i = sysconf(_SC_NPROCESSORS_CONF);
1462 pos += sprintf(pos, "%d%%cpu", i*100);
1465 // If a processor goes idle it's powered down and its idle ticks don't
1466 // advance, so calculate idle time as potential time - used.
1467 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1470 ll = stats[3] = stats[11] = 0;
1471 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1472 stats[3] = now - llabs(ll);
1474 for (i = 0; i<8; i++) {
1475 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1476 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1478 lines = header_line(lines, 0);
1480 struct strawberry *fields;
1483 memset(&tb, 0, sizeof(struct carveup));
1484 pos = stpcpy(toybuf, "Totals:");
1485 for (fields = TT.fields; fields; fields = fields->next) {
1486 long long ll, bits = 0;
1487 int slot = typos[fields->which].slot&63;
1489 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1490 ll = 1LL<<fields->which;
1491 if (bits&ll) continue;
1493 for (i=0; i<mix.count; i++)
1494 tb.slot[slot] += mix.tb[i]->slot[slot];
1495 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1496 " %s: %*s,", typos[fields->which].name,
1497 fields->len, string_field(&tb, fields));
1500 lines = header_line(lines, 0);
1503 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1504 for (i = 0, is = ' '; *pos; pos++) {
1507 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1509 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1512 lines = header_line(lines, 1);
1514 if (!recalc && !(toys.optflags&FLAG_b))
1515 printf("\033[%dH\033[J", 1+TT.height-lines);
1518 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1519 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1520 show_ps(mix.tb[i+topoff]);
1523 if (TT.top.n && !--TT.top.n) {
1529 if (timeout<=now) timeout = new.whence+TT.top.d;
1530 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1532 // In batch mode, we ignore the keyboard.
1533 if (toys.optflags&FLAG_b) {
1534 msleep(timeout-now);
1535 // Make an obvious gap between datasets.
1540 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1541 if (i==-1 || i==3 || toupper(i)=='Q') {
1547 // Flush unknown escape sequences.
1548 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1552 } else if (toupper(i)=='R')
1553 ((struct strawberry *)TT.kfields)->reverse *= -1;
1556 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1557 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1558 // KEY_UP is 0, so at end of strchr
1559 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1562 if (i == KEY_UP) topoff--;
1563 else if (i == KEY_DOWN) topoff++;
1564 else if (i == KEY_PGDN) topoff += lines;
1565 else if (i == KEY_PGUP) topoff -= lines;
1566 if (topoff<0) topoff = 0;
1567 if (topoff>mix.count) topoff = mix.count;
1574 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1578 if (!(toys.optflags&FLAG_b)) tty_reset();
1581 static void top_setup(char *defo, char *defk)
1584 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1586 TT.time = millitime();
1587 set_terminal(0, 1, 0);
1588 sigatexit(tty_sigreset);
1589 xsignal(SIGWINCH, generic_signal);
1590 printf("\033[?25l\033[0m");
1594 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1595 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1596 TT.match_process = shared_match_process;
1598 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1599 dlist_terminate(TT.fields);
1601 // First (dummy) sort field is overwritten by setsort()
1602 default_ko("-S", &TT.kfields, 0, 0);
1603 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1604 dlist_terminate(TT.kfields);
1605 setsort(TT.top.s-1);
1610 // usage: [-h HEADER] -o OUTPUT -k SORT
1612 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1613 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1614 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1615 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1617 struct strawberry *fields = TT.fields;
1619 fields = fields->next->next;
1620 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1623 top_common(merge_deltas);
1628 #include "generated/flags.h"
1630 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1632 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1633 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1635 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1638 void iotop_main(void)
1640 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1642 if (toys.optflags&FLAG_K) TT.forcek++;
1644 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1645 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1648 top_common(iotop_filter);
1651 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1652 // context, so force pgrep's flags on even when building pkill standalone.
1653 // (All the pgrep/pkill functions drop out when building ps standalone.)
1655 #define CLEANUP_iotop
1657 #include "generated/flags.h"
1660 struct regex_list *next;
1664 static void do_pgk(struct carveup *tb)
1666 if (TT.pgrep.signal) {
1667 if (kill(*tb->slot, TT.pgrep.signal)) {
1668 char *s = num_to_sig(TT.pgrep.signal);
1670 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1671 perror_msg("%s->%lld", s, *tb->slot);
1674 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1675 printf("%lld", *tb->slot);
1676 if (toys.optflags&FLAG_l)
1677 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1679 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1683 static void match_pgrep(struct carveup *tb)
1686 struct regex_list *reg;
1687 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1689 // Never match ourselves.
1690 if (TT.pgrep.self == *tb->slot) return;
1692 if (TT.pgrep.regexes) {
1693 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1694 if (regexec(®->reg, name, 1, &match, 0)) continue;
1695 if (toys.optflags&FLAG_x)
1696 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1699 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1702 // pgrep should return success if there's a match.
1705 // Repurpose a field for -c count.
1707 if (toys.optflags&(FLAG_n|FLAG_o)) {
1708 long long ll = tb->slot[SLOT_starttime];
1710 if (toys.optflags&FLAG_o) ll *= -1;
1711 if (TT.time && TT.time>ll) return;
1713 free(TT.pgrep.snapshot);
1714 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1718 static int pgrep_match_process(long long *slot)
1720 int match = shared_match_process(slot);
1722 return (toys.optflags&FLAG_v) ? !match : match;
1725 void pgrep_main(void)
1728 struct regex_list *reg;
1730 TT.pgrep.self = getpid();
1732 // No signal names start with "L", so no need for "L: " parsing.
1733 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1734 error_exit("bad -L '%s'", TT.pgrep.L);
1736 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1737 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1738 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1739 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1740 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1741 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1742 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1744 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1745 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1746 if (!toys.optc) help_exit("No PATTERN");
1748 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1749 for (arg = toys.optargs; *arg; arg++) {
1750 reg = xmalloc(sizeof(struct regex_list));
1751 xregcomp(®->reg, *arg, REG_EXTENDED);
1752 reg->next = TT.pgrep.regexes;
1753 TT.pgrep.regexes = reg;
1755 TT.match_process = pgrep_match_process;
1756 TT.show_process = (void *)match_pgrep;
1758 // pgrep should return failure if there are no matches.
1761 dirtree_read("/proc", get_ps);
1762 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1763 if (TT.pgrep.snapshot) {
1764 do_pgk(TT.pgrep.snapshot);
1765 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1767 if (TT.pgrep.d) xputc('\n');
1770 #define CLEANUP_pgrep
1772 #include "generated/flags.h"
1774 void pkill_main(void)
1776 char **args = toys.optargs;
1778 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1779 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1780 if (toys.optflags & FLAG_V) TT.tty = 1;