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. Posix doesn't say truncated fields
17 * should end with "+" but it's pretty common behavior.
19 * Posix defines -o ADDR as "The address of the process" but the process
20 * start address is a constant on any elf system with mmu. The procps ADDR
21 * field always prints "-" with an alignment of 1, which is why it has 11
22 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
23 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
24 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
25 * which changes -l by removing the "F" column and swapping RSS for ADDR,
26 * leaving 9 chars for cmd, so we're using that as our -l output.
28 * Added a bunch of new -o fields posix doesn't mention, and we don't
29 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
30 * output argv[0] unmodified for -o comm or -o args (but procps violates
31 * posix for -o comm anyway, it's stat[2] not argv[0]).
33 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
34 * files (why they're not globally readable when the rest of proc
35 * data is...?) and get a global I/O picture. Normal top is NOT,
36 * even though you can -o AIO there, to give sysadmins the option
37 * to reduce security exposure.)
39 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
40 * TODO: switch -fl to -y
41 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
42 * TODO: iotop: Window size change: respond immediately. Why not padding
43 * at right edge? (Not adjusting to screen size at all? Header wraps?)
44 * TODO: top: thread support and SMP
45 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
47 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
48 // stayroot because iotop needs root to read other process' proc/$$/io
49 USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
50 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
51 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
52 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
58 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
62 Which processes to show (selections may be comma separated lists):
65 -a Processes with terminals that aren't session leaders
66 -d All processes that aren't session leaders
68 -g Belonging to GROUPs
69 -G Belonging to real GROUPs (before sgid)
71 -P Parent PIDs (--ppid)
73 -t Attached to selected TTYs
76 -U Owned by real USERs (before suid)
80 -k Sort FIELDs in +increasing or -decreasting order (--sort)
81 -M Measure field widths (expanding as necessary)
82 -n Show numeric USER and GROUP
83 -w Wide output (don't truncate fields)
85 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
87 -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
88 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
89 -o Output FIELDs instead of defaults, each with optional :size and =title
90 -O Add FIELDS to defaults
93 Command line -o fields:
95 ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
96 CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
97 COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
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", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
343 {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
344 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
345 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
346 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
349 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
350 {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
351 {"ARGS", -27, -6}, {"CMD", -15, -1},
354 {"UID", 5, SLOT_uid}, {"USER", -12, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
355 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
356 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
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 (which==PS_ARGS || which==PS_COMM) {
450 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
451 if (out[i] == '/') s = out+i+1;
454 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
457 } else if (which <= PS_RGROUP) {
458 sprintf(out, "%lld", ll);
460 if (which > PS_RUSER) {
461 struct group *gr = bufgetgrgid(ll);
463 if (gr) out = gr->gr_name;
465 struct passwd *pw = bufgetpwuid(ll);
467 if (pw) out = pw->pw_name;
472 } else if (which <= PS_TIME_) {
473 int unit = 60, pad = 2, j = TT.ticks;
476 if (which!=PS_TIME_) unit *= 60*24;
478 // top adjusts slot[SLOT_upticks], we want original meaning.
479 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
482 // Output days-hours:mins:secs, skipping non-required fields with zero
483 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
484 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
485 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
487 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
489 if ((*s = "-::"[j])) s++;
494 if (which==PS_TIME_ && s-out<8)
495 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
497 // Percentage displays
498 } else if (which <= PS__CPU) {
499 ll = slot[sl&63]*1000;
500 if (which==PS__VSZ || which==PS__MEM)
501 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
502 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
504 if (which==PS_C) sl += 5;
505 sprintf(out, "%d", sl/10);
506 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
509 } else if (which <= PS_DIO) {
510 ll = slot[typos[which].slot];
511 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
512 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
513 else human_readable(out, ll, 0);
515 // Posix doesn't specify what flags should say. Man page says
516 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
517 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
518 else if (which==PS_S || which==PS_STAT) {
521 if (which==PS_STAT) {
522 // TODO l = multithreaded
523 if (slot[SLOT_nice]<0) *s++ = '<';
524 else if (slot[SLOT_nice]>0) *s++ = 'N';
525 if (slot[SLOT_sid]==*slot) *s++ = 's';
526 if (slot[SLOT_vmlck]) *s++ = 'L';
527 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
530 } else if (which==PS_STIME) {
531 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
533 // Padding behavior's a bit odd: default field size is just hh:mm.
534 // Increasing stime:size reveals more data at left until full,
535 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
536 // then add :ss on right for :19.
537 strftime(out, 260, "%F %T", localtime(&t));
538 out = out+strlen(out)-3-abs(field->len);
539 if (out<buf) out = buf;
541 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
542 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
547 // Display process data that get_ps() read from /proc, formatting with TT.fields
548 static void show_ps(void *p)
550 struct carveup *tb = p;
551 struct strawberry *field;
552 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
554 // Loop through fields to display
555 for (field = TT.fields; field; field = field->next) {
556 char *out = string_field(tb, field);
558 // Output the field, appropriately padded
560 // Minimum one space between each field
561 if (field != TT.fields) {
566 // Don't truncate number fields, but try to reclaim extra offset from later
567 // fields that can naturally be shorter
568 abslen = abs(field->len);
569 sign = field->len<0 ? -1 : 1;
570 olen = (TT.tty) ? utf8len(out) : strlen(out);
571 if ((field->which<=PS_BIT || (toys.optflags&FLAG_w)) && olen>abslen) {
572 // overflow but remember by how much
573 extra += olen-abslen;
575 } else if (extra && olen<abslen) {
576 int unused = abslen-olen;
578 // If later fields have slack space, take back overflow
579 if (unused>extra) unused = extra;
583 if (abslen>width) abslen = width;
587 // If last field is left justified, no trailing spaces.
588 if (!field->next && sign<0) pad = -(len = width);
590 // If we truncated a left-justified field, show + instead of last char
591 if (olen>len && len>1 && sign<0) {
598 if (TT.tty) width -= draw_trim(out, pad, len);
599 else width -= printf("%*.*s", pad, len, out);
600 if (!abslen) putchar('+');
603 xputc(TT.time ? '\r' : '\n');
606 // dirtree callback: read data about process to display, store, or discard it.
607 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
608 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
609 static int get_ps(struct dirtree *new)
612 char *name; // Path under /proc/$PID directory
613 long long bits; // Only fetch extra data if an -o field is displaying it
615 // sources for carveup->offset[] data
616 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
617 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
620 struct carveup *tb = (void *)toybuf;
621 long long *slot = tb->slot;
622 char *name, *s, *buf = tb->str, *end = 0;
626 // Recurse one level into /proc children, skip non-numeric entries
628 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
629 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
631 memset(slot, 0, sizeof(tb->slot));
632 tb->slot[SLOT_tid] = *slot = atol(new->name);
633 if (TT.threadparent && TT.threadparent->extra)
634 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
635 fd = dirtree_parentfd(new);
638 sprintf(buf, "%lld/stat", *slot);
639 if (!readfileat(fd, buf, buf, &len)) return 0;
641 // parse oddball fields (name and state). Name can have embedded ')' so match
642 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
643 // All remaining fields should be numeric.
644 if (!(name = strchr(buf, '('))) return 0;
645 for (s = ++name; *s; s++) if (*s == ')') end = s;
646 if (!end || end-name>255) return 0;
648 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
649 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
650 for (j = 1; j<SLOT_count; j++)
651 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
653 // Now we've read the data, move status and name right after slot[] array,
654 // and convert low chars to ? for non-tty display while we're at it.
655 for (i = 0; i<end-name; i++)
656 if ((tb->str[i] = name[i]) < ' ')
657 if (!TT.tty) tb->str[i] = '?';
660 len = sizeof(toybuf)-(buf-toybuf);
662 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
663 // or numeric wchan, and the remaining two are always zero), and vmlck into
664 // 18 (which is "obsolete, always 0" from stat)
665 slot[SLOT_uid] = new->st.st_uid;
666 slot[SLOT_gid] = new->st.st_gid;
668 // TIME and TIME+ use combined value, ksort needs 'em added.
669 slot[SLOT_utime] += slot[SLOT_stime];
670 slot[SLOT_utime2] = slot[SLOT_utime];
672 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
673 // and save ruid, rgid, and vmlck.
674 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
675 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
679 sprintf(buf, "%lld/status", *slot);
680 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
681 s = strafter(buf, "\nUid:");
682 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
683 s = strafter(buf, "\nGid:");
684 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
685 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
686 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
689 // Do we need to read "io"?
690 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
693 sprintf(buf, "%lld/io", *slot);
694 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
695 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
696 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
697 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
698 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
699 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
700 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
703 // We now know enough to skip processes we don't care about.
704 if (TT.match_process && !TT.match_process(slot)) return 0;
706 // /proc data is generated as it's read, so for maximum accuracy on slow
707 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
709 slot[SLOT_uptime] = TT.si.uptime;
710 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
712 // Do we need to read "statm"?
713 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
716 sprintf(buf, "%lld/statm", *slot);
717 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
719 for (s = buf, i=0; i<3; i++)
720 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
724 // Do we need to read "exe"?
725 if (TT.bits&_PS_BIT) {
728 sprintf(buf, "%lld/exe", *slot);
729 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
730 if (buf[4] == 1) slot[SLOT_bits] = 32;
731 else if (buf[4] == 2) slot[SLOT_bits] = 64;
735 // Do we need Android scheduling policy?
736 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
738 // Fetch string data while parentfd still available, appending to buf.
739 // (There's well over 3k of toybuf left. We could dynamically malloc, but
740 // it'd almost never get used, querying length of a proc file is awkward,
741 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
742 slot[SLOT_argv0len] = 0;
743 for (j = 0; j<ARRAY_LEN(fetch); j++) {
744 tb->offset[j] = buf-(tb->str);
745 if (!(TT.bits&fetch[j].bits)) {
750 // Determine remaining space, reserving minimum of 256 bytes/field and
751 // 260 bytes scratch space at the end (for output conversion later).
752 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
753 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
755 // For exe we readlink instead of read contents
757 struct carveup *ptb = 0;
760 // Thread doesn't have exe or argv[0], so use parent's
761 if (TT.threadparent && TT.threadparent->extra)
762 ptb = (void *)TT.threadparent->extra;
764 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
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 && (len = readlinkat0(fd, buf, buf, len)))
798 // Couldn't find it, try all the tty drivers.
800 FILE *fp = fopen("/proc/tty/drivers", "r");
801 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
804 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
805 // TODO: we could parse the minor range too.
806 if (tty_major == maj) {
808 len += sprintf(buf+len, "%d", min);
809 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
817 // Really couldn't find it, so just show major:minor.
818 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
822 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
825 // Data we want is in a file.
826 // Last length saved in slot[] is command line (which has embedded NULs)
830 // When command has no arguments, don't space over the NUL
831 if (readfileat(fd, buf, buf, &len) && len>0) {
833 // Trim trailing whitespace and NUL bytes
835 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
838 // Turn NUL to space, other low ascii to ? (in non-tty mode)
839 // cmdline has a trailing NUL that we don't want to turn to space.
840 for (i=0; i<len-1; i++) {
846 } else if (!TT.tty && c<' ') c = '?';
849 } else *buf = len = 0;
851 // Store end of argv[0] so ARGS and CMDLINE can differ.
852 // We do it for each file string slot but last is cmdline, which sticks.
853 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
856 // Above calculated/retained len, so we don't need to re-strlen.
861 if (TT.show_process && !TT.threadparent) {
867 // If we need to sort the output, add it to the list and return.
868 s = xmalloc(buf-toybuf);
869 new->extra = (long)s;
870 memcpy(s, toybuf, buf-toybuf);
875 static int get_threads(struct dirtree *new)
879 unsigned pid, kcount;
881 if (!new->parent) return get_ps(new);
882 pid = atol(new->name);
884 TT.threadparent = new;
891 // Recurse down into tasks, retaining thread groups.
892 // Disable show_process at least until we can calculate tcount
894 sprintf(toybuf, "/proc/%u/task", pid);
895 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
897 kcount = TT.kcount-kcount+1;
898 tb = (void *)new->extra;
899 tb->slot[SLOT_tcount] = kcount;
901 // Fill out tid and thread count for each entry in group
902 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
903 tb = (void *)dt->extra;
904 tb->slot[SLOT_pid] = pid;
905 tb->slot[SLOT_tcount] = kcount;
909 if (!TT.show_process) return DIRTREE_SAVE;
910 TT.show_process((void *)new->extra);
914 new = dt->child->next;
915 TT.show_process((void *)dt->child->extra);
924 static char *parse_ko(void *data, char *type, int length)
926 struct strawberry *field;
927 char *width, *title, *end, *s;
930 // Get title, length of title, type, end of type, and display width
932 // Chip off =name to display
933 if ((end = strchr(type, '=')) && length>(end-type)) {
935 length -= (end-type)+1;
941 // Chip off :width to display
942 if ((width = strchr(type, ':')) && width<end) {
943 if (!title) length = width-type;
946 // Allocate structure, copy title
947 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
949 memcpy(field->title = field->forever, title, length);
950 field->title[field->len = length] = 0;
954 field->len = strtol(++width, &title, 10);
955 if (!isdigit(*width) || title != end) return title;
961 if (*type == '-') field->reverse = -1;
962 else if (*type != '+') type--;
964 for (i = 0; i<ARRAY_LEN(typos); i++) {
966 for (j = 0; j<2; j++) {
967 if (!j) s = typos[i].name;
968 // posix requires alternate names for some fields
969 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
970 PS_VSZ, PS_USER, 0}, i))) continue;
972 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
974 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
978 if (i==ARRAY_LEN(typos)) return type;
979 if (!field->title) field->title = typos[field->which].name;
980 if (!field->len) field->len = typos[field->which].width;
981 else if (typos[field->which].width<0) field->len *= -1;
982 dlist_add_nomalloc(data, (void *)field);
987 static long long get_headers(struct strawberry *fields, char *buf, int blen)
992 for (; fields; fields = fields->next) {
993 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
995 bits |= 1LL<<fields->which;
1001 // Parse -p -s -t -u -U -g -G
1002 static char *parse_rest(void *data, char *str, int len)
1004 struct ptr_len *pl = (struct ptr_len *)data;
1009 // Allocate next chunk of data
1011 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1013 // Parse numerical input
1014 if (isdigit(*str)) {
1015 ll[pl->len] = xstrtol(str, &end, 10);
1016 if (end==(len+str)) num++;
1017 // For pkill, -s 0 represents pkill's session id.
1018 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1021 if (pl==&TT.pp || pl==&TT.ss) {
1022 if (num && ll[pl->len]>0) {
1027 } else if (pl==&TT.tt) {
1028 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1030 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1031 if (strstart(&str, "pts/")) {
1034 } else if (strstart(&str, "tty")) len -= 3;
1036 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1039 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1040 memcpy(end, str, len);
1043 ll[pl->len++] = st.st_rdev;
1047 } else if (len<255) {
1056 memcpy(name, str, len);
1058 if (pl==&TT.gg || pl==&TT.GG) {
1059 struct group *gr = getgrnam(name);
1061 ll[pl->len++] = gr->gr_gid;
1065 } else if (pl==&TT.uu || pl==&TT.UU) {
1066 struct passwd *pw = getpwnam(name);
1068 ll[pl->len++] = pw->pw_uid;
1080 static int ksort(void *aa, void *bb)
1082 struct strawberry *field;
1083 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1086 for (field = TT.kfields; field && !ret; field = field->next) {
1087 slot = typos[field->which].slot;
1089 // Can we do numeric sort?
1091 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1092 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1095 // fallback to string sort
1097 memccpy(toybuf, string_field(ta, field), 0, 2048);
1099 ret = strcmp(toybuf, string_field(tb, field));
1101 ret *= field->reverse;
1107 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1110 struct dirtree *next = dt->next;
1112 if (dt->extra) *(tb++) = (void *)dt->extra;
1113 if (dt->child) tb = collate_leaves(tb, dt->child);
1121 static struct carveup **collate(int count, struct dirtree *dt)
1123 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1125 collate_leaves(tbsort, dt);
1130 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1132 struct arg_list def;
1134 memset(&def, 0, sizeof(struct arg_list));
1136 comma_args(arg ? arg : &def, fields, err, parse_ko);
1139 static void shared_main(void)
1143 TT.ticks = sysconf(_SC_CLK_TCK);
1145 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1147 terminal_size(&TT.width, &TT.height);
1150 // find controlling tty, falling back to /dev/tty if none
1151 for (i = 0; !TT.tty && i<4; i++) {
1155 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1157 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1158 if (i==3) close(fd);
1169 if (toys.optflags&FLAG_w) TT.width = 99999;
1172 // parse command line options other than -o
1173 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1174 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1175 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1176 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1177 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1178 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1179 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1180 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1181 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1182 dlist_terminate(TT.kfields);
1184 // It's undocumented, but traditionally extra arguments are extra -p args
1185 for (arg = toys.optargs; *arg; arg++)
1186 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit_raw(*arg);
1188 // Figure out which fields to display
1189 not_o = "%sTTY,TIME,CMD";
1190 if (toys.optflags&FLAG_f)
1191 sprintf(not_o = toybuf+128,
1192 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=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 sprintf(not_o = toybuf+128,
1198 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1199 (toys.optflags&FLAG_T) ? "CMD" : "NAME");
1200 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1202 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1203 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1204 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1207 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1208 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1209 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1211 dlist_terminate(TT.fields);
1213 // -f and -n change the meaning of some fields
1214 if (toys.optflags&(FLAG_f|FLAG_n)) {
1215 struct strawberry *ever;
1217 for (ever = TT.fields; ever; ever = ever->next) {
1218 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1219 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1224 // Calculate seen fields bit array, and if we aren't deferring printing
1225 // print headers now (for low memory/nommu systems).
1226 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1227 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1228 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = show_ps;
1229 TT.match_process = ps_match_process;
1230 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1231 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1232 ? get_threads : get_ps);
1234 if (toys.optflags&(FLAG_k|FLAG_M)) {
1235 struct carveup **tbsort = collate(TT.kcount, dt);
1237 if (toys.optflags&FLAG_M) {
1238 for (i = 0; i<TT.kcount; i++) {
1239 struct strawberry *field;
1241 for (field = TT.fields; field; field = field->next) {
1242 int len = strlen(string_field(tbsort[i], field));
1244 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1248 // Now that we've recalculated field widths, re-pad headers again
1249 get_headers(TT.fields, toybuf, sizeof(toybuf));
1250 printf("%.*s\n", TT.width, toybuf);
1253 if (toys.optflags&FLAG_k)
1254 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1255 for (i = 0; i<TT.kcount; i++) {
1259 if (CFG_TOYBOX_FREE) free(tbsort);
1262 if (CFG_TOYBOX_FREE) {
1271 llist_traverse(TT.fields, free);
1277 #include "generated/flags.h"
1279 // select which of the -o fields to sort by
1280 static void setsort(int pos)
1282 struct strawberry *field, *going2;
1287 for (field = TT.fields; field; field = field->next) {
1288 if ((TT.sortpos = i++)<pos && field->next) continue;
1289 going2 = TT.kfields;
1290 going2->which = field->which;
1291 going2->len = field->len;
1296 // If we have both, adjust slot[deltas[]] to be relative to previous
1297 // measurement rather than process start. Stomping old.data is fine
1298 // because we free it after displaying.
1299 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1301 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1302 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1305 for (i = 0; i<ARRAY_LEN(deltas); i++)
1306 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1307 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1312 static int header_line(int line, int rev)
1314 if (!line) return 0;
1316 if (toys.optflags&FLAG_b) rev = 0;
1318 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1319 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1320 rev ? "\033[0m" : "");
1325 static long long millitime(void)
1329 clock_gettime(CLOCK_MONOTONIC, &ts);
1330 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1333 static void top_common(
1334 int (*filter)(long long *oslot, long long *nslot, int milis))
1336 long long timeout = 0, now, stats[16];
1338 struct carveup **tb;
1341 } plist[2], *plold, *plnew, old, new, mix;
1342 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1343 "iow", "irq", "sirq", "host"};
1346 int i, lines, topoff = 0, done = 0;
1348 toys.signal = SIGWINCH;
1349 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1351 memset(plist, 0, sizeof(plist));
1352 memset(stats, 0, sizeof(stats));
1357 plold = plist+(tock++&1);
1358 plnew = plist+(tock&1);
1359 plnew->whence = millitime();
1360 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1361 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1362 ? get_threads : get_ps);
1363 plnew->tb = collate(plnew->count = TT.kcount, dt);
1366 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1367 long long *st = stats+8*(tock&1);
1369 // user nice system idle iowait irq softirq host
1370 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1371 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1374 // First time, wait a quarter of a second to collect a little delta data.
1380 // Collate old and new into "mix", depends on /proc read in pid sort order
1383 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1386 while (old.count || new.count) {
1387 struct carveup *otb = *old.tb, *ntb = *new.tb;
1389 // If we just have old for this process, it exited. Discard it.
1390 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1397 // If we just have new, use it verbatim
1398 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1401 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1402 mix.tb[mix.count] = otb;
1412 // Don't re-fetch data if it's not time yet, just re-display existing data.
1417 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1418 if (!(toys.optflags&FLAG_b)) {
1419 printf("\033[H\033[J");
1422 terminal_probesize(&TT.width, &TT.height);
1427 if (recalc && !(toys.optflags&FLAG_q)) {
1428 // Display "top" header.
1429 if (*toys.which->name == 't') {
1430 struct strawberry alluc;
1431 long long ll, up = 0;
1435 // Count running, sleeping, stopped, zombie processes.
1437 memset(run, 0, sizeof(run));
1438 for (i = 0; i<mix.count; i++)
1439 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1441 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1442 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1443 lines = header_line(lines, 0);
1445 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1446 for (i=0; i<6; i++) {
1447 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1448 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1449 run[i] = pos ? atol(pos) : 0;
1452 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1453 run[0], run[0]-run[1], run[1], run[2]);
1454 lines = header_line(lines, 0);
1456 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1457 run[4], run[4]-run[5], run[5], run[3]);
1458 lines = header_line(lines, 0);
1462 i = sysconf(_SC_NPROCESSORS_CONF);
1463 pos += sprintf(pos, "%d%%cpu", i*100);
1466 // If a processor goes idle it's powered down and its idle ticks don't
1467 // advance, so calculate idle time as potential time - used.
1468 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1471 ll = stats[3] = stats[11] = 0;
1472 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1473 stats[3] = now - llabs(ll);
1475 for (i = 0; i<8; i++) {
1476 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1477 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1479 lines = header_line(lines, 0);
1481 struct strawberry *fields;
1484 memset(&tb, 0, sizeof(struct carveup));
1485 pos = stpcpy(toybuf, "Totals:");
1486 for (fields = TT.fields; fields; fields = fields->next) {
1487 long long ll, bits = 0;
1488 int slot = typos[fields->which].slot&63;
1490 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1491 ll = 1LL<<fields->which;
1492 if (bits&ll) continue;
1494 for (i=0; i<mix.count; i++)
1495 tb.slot[slot] += mix.tb[i]->slot[slot];
1496 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1497 " %s: %*s,", typos[fields->which].name,
1498 fields->len, string_field(&tb, fields));
1501 lines = header_line(lines, 0);
1504 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1505 for (i = 0, is = ' '; *pos; pos++) {
1508 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1510 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1513 lines = header_line(lines, 1);
1515 if (!recalc && !(toys.optflags&FLAG_b))
1516 printf("\033[%dH\033[J", 1+TT.height-lines);
1519 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1520 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1521 show_ps(mix.tb[i+topoff]);
1524 if (TT.top.n && !--TT.top.n) {
1530 if (timeout<=now) timeout = new.whence+TT.top.d;
1531 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1533 // In batch mode, we ignore the keyboard.
1534 if (toys.optflags&FLAG_b) {
1535 msleep(timeout-now);
1536 // Make an obvious gap between datasets.
1541 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1542 if (i==-1 || i==3 || toupper(i)=='Q') {
1548 // Flush unknown escape sequences.
1549 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1553 } else if (toupper(i)=='R')
1554 ((struct strawberry *)TT.kfields)->reverse *= -1;
1557 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1558 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1559 // KEY_UP is 0, so at end of strchr
1560 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1563 if (i == KEY_UP) topoff--;
1564 else if (i == KEY_DOWN) topoff++;
1565 else if (i == KEY_PGDN) topoff += lines;
1566 else if (i == KEY_PGUP) topoff -= lines;
1567 if (topoff<0) topoff = 0;
1568 if (topoff>mix.count) topoff = mix.count;
1575 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1579 if (!(toys.optflags&FLAG_b)) tty_reset();
1582 static void top_setup(char *defo, char *defk)
1585 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1587 TT.time = millitime();
1588 set_terminal(0, 1, 0);
1589 sigatexit(tty_sigreset);
1590 xsignal(SIGWINCH, generic_signal);
1591 printf("\033[?25l\033[0m");
1595 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1596 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1597 TT.match_process = shared_match_process;
1599 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1600 dlist_terminate(TT.fields);
1602 // First (dummy) sort field is overwritten by setsort()
1603 default_ko("-S", &TT.kfields, 0, 0);
1604 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1605 dlist_terminate(TT.kfields);
1606 setsort(TT.top.s-1);
1611 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,%s",
1612 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1613 toys.optflags&FLAG_H ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
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(void *p)
1685 struct carveup *tb = p;
1687 struct regex_list *reg;
1688 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1690 // Never match ourselves.
1691 if (TT.pgrep.self == *tb->slot) return;
1693 if (TT.pgrep.regexes) {
1694 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1695 if (regexec(®->reg, name, 1, &match, 0)) continue;
1696 if (toys.optflags&FLAG_x)
1697 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1700 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1703 // pgrep should return success if there's a match.
1706 // Repurpose a field for -c count.
1708 if (toys.optflags&(FLAG_n|FLAG_o)) {
1709 long long ll = tb->slot[SLOT_starttime];
1711 if (toys.optflags&FLAG_o) ll *= -1;
1712 if (TT.time && TT.time>ll) return;
1714 free(TT.pgrep.snapshot);
1715 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1719 static int pgrep_match_process(long long *slot)
1721 int match = shared_match_process(slot);
1723 return (toys.optflags&FLAG_v) ? !match : match;
1726 void pgrep_main(void)
1729 struct regex_list *reg;
1731 TT.pgrep.self = getpid();
1733 // No signal names start with "L", so no need for "L: " parsing.
1734 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1735 error_exit("bad -L '%s'", TT.pgrep.L);
1737 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1738 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1739 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1740 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1741 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1742 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1743 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1745 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1746 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1747 if (!toys.optc) help_exit("No PATTERN");
1749 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1750 for (arg = toys.optargs; *arg; arg++) {
1751 reg = xmalloc(sizeof(struct regex_list));
1752 xregcomp(®->reg, *arg, REG_EXTENDED);
1753 reg->next = TT.pgrep.regexes;
1754 TT.pgrep.regexes = reg;
1756 TT.match_process = pgrep_match_process;
1757 TT.show_process = match_pgrep;
1759 // pgrep should return failure if there are no matches.
1762 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1763 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1764 if (TT.pgrep.snapshot) {
1765 do_pgk(TT.pgrep.snapshot);
1766 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1768 if (TT.pgrep.d) xputc('\n');
1771 #define CLEANUP_pgrep
1773 #include "generated/flags.h"
1775 void pkill_main(void)
1777 char **args = toys.optargs;
1779 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1780 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1781 if (toys.optflags & FLAG_V) TT.tty = 1;