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) {
593 // If we truncated a left-justified field, show + instead of last char
594 if (olen>len && len>1 && sign<0) {
597 if (field->next) pad++;
601 if (TT.tty) width -= draw_trim(out, pad, len);
602 else width -= printf("%*.*s", pad, len, out);
603 if (!abslen) putchar('+');
606 xputc(TT.time ? '\r' : '\n');
609 // dirtree callback: read data about process to display, store, or discard it.
610 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
611 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
612 static int get_ps(struct dirtree *new)
615 char *name; // Path under /proc/$PID directory
616 long long bits; // Only fetch extra data if an -o field is displaying it
618 // sources for carveup->offset[] data
619 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
620 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
623 struct carveup *tb = (void *)toybuf;
624 long long *slot = tb->slot;
625 char *name, *s, *buf = tb->str, *end = 0;
629 // Recurse one level into /proc children, skip non-numeric entries
631 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
632 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
634 memset(slot, 0, sizeof(tb->slot));
635 tb->slot[SLOT_tid] = *slot = atol(new->name);
636 if (TT.threadparent && TT.threadparent->extra)
637 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
638 fd = dirtree_parentfd(new);
641 sprintf(buf, "%lld/stat", *slot);
642 if (!readfileat(fd, buf, buf, &len)) return 0;
644 // parse oddball fields (name and state). Name can have embedded ')' so match
645 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
646 // All remaining fields should be numeric.
647 if (!(name = strchr(buf, '('))) return 0;
648 for (s = ++name; *s; s++) if (*s == ')') end = s;
649 if (!end || end-name>255) return 0;
651 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
652 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
653 for (j = 1; j<SLOT_count; j++)
654 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
656 // Now we've read the data, move status and name right after slot[] array,
657 // and convert low chars to ? for non-tty display while we're at it.
658 for (i = 0; i<end-name; i++)
659 if ((tb->str[i] = name[i]) < ' ')
660 if (!TT.tty) tb->str[i] = '?';
663 len = sizeof(toybuf)-(buf-toybuf);
665 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
666 // or numeric wchan, and the remaining two are always zero), and vmlck into
667 // 18 (which is "obsolete, always 0" from stat)
668 slot[SLOT_uid] = new->st.st_uid;
669 slot[SLOT_gid] = new->st.st_gid;
671 // TIME and TIME+ use combined value, ksort needs 'em added.
672 slot[SLOT_utime] += slot[SLOT_stime];
673 slot[SLOT_utime2] = slot[SLOT_utime];
675 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
676 // and save ruid, rgid, and vmlck.
677 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
678 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
682 sprintf(buf, "%lld/status", *slot);
683 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
684 s = strafter(buf, "\nUid:");
685 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
686 s = strafter(buf, "\nGid:");
687 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
688 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
689 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
692 // Do we need to read "io"?
693 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
696 sprintf(buf, "%lld/io", *slot);
697 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
698 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
699 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
700 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
701 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
702 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
703 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
706 // We now know enough to skip processes we don't care about.
707 if (TT.match_process && !TT.match_process(slot)) return 0;
709 // /proc data is generated as it's read, so for maximum accuracy on slow
710 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
712 slot[SLOT_uptime] = TT.si.uptime;
713 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
715 // Do we need to read "statm"?
716 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
719 sprintf(buf, "%lld/statm", *slot);
720 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
722 for (s = buf, i=0; i<3; i++)
723 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
727 // Do we need to read "exe"?
728 if (TT.bits&_PS_BIT) {
731 sprintf(buf, "%lld/exe", *slot);
732 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
733 if (buf[4] == 1) slot[SLOT_bits] = 32;
734 else if (buf[4] == 2) slot[SLOT_bits] = 64;
738 // Do we need Android scheduling policy?
739 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
741 // Fetch string data while parentfd still available, appending to buf.
742 // (There's well over 3k of toybuf left. We could dynamically malloc, but
743 // it'd almost never get used, querying length of a proc file is awkward,
744 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
745 slot[SLOT_argv0len] = 0;
746 for (j = 0; j<ARRAY_LEN(fetch); j++) {
747 tb->offset[j] = buf-(tb->str);
748 if (!(TT.bits&fetch[j].bits)) {
753 // Determine remaining space, reserving minimum of 256 bytes/field and
754 // 260 bytes scratch space at the end (for output conversion later).
755 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
756 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
758 // For exe we readlink instead of read contents
760 struct carveup *ptb = 0;
763 // Thread doesn't have exe or argv[0], so use parent's
764 if (TT.threadparent && TT.threadparent->extra)
765 ptb = (void *)TT.threadparent->extra;
767 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
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 && (len = readlinkat0(fd, buf, buf, len)))
801 // Couldn't find it, try all the tty drivers.
803 FILE *fp = fopen("/proc/tty/drivers", "r");
804 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
807 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
808 // TODO: we could parse the minor range too.
809 if (tty_major == maj) {
811 len += sprintf(buf+len, "%d", min);
812 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
820 // Really couldn't find it, so just show major:minor.
821 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
825 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
828 // Data we want is in a file.
829 // Last length saved in slot[] is command line (which has embedded NULs)
833 // When command has no arguments, don't space over the NUL
834 if (readfileat(fd, buf, buf, &len) && len>0) {
836 // Trim trailing whitespace and NUL bytes
838 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
841 // Turn NUL to space, other low ascii to ? (in non-tty mode)
842 // cmdline has a trailing NUL that we don't want to turn to space.
843 for (i=0; i<len-1; i++) {
849 } else if (!TT.tty && c<' ') c = '?';
852 } else *buf = len = 0;
854 // Store end of argv[0] so ARGS and CMDLINE can differ.
855 // We do it for each file string slot but last is cmdline, which sticks.
856 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
859 // Above calculated/retained len, so we don't need to re-strlen.
864 if (TT.show_process && !TT.threadparent) {
870 // If we need to sort the output, add it to the list and return.
871 s = xmalloc(buf-toybuf);
872 new->extra = (long)s;
873 memcpy(s, toybuf, buf-toybuf);
878 static int get_threads(struct dirtree *new)
882 unsigned pid, kcount;
884 if (!new->parent) return get_ps(new);
885 pid = atol(new->name);
887 TT.threadparent = new;
894 // Recurse down into tasks, retaining thread groups.
895 // Disable show_process at least until we can calculate tcount
897 sprintf(toybuf, "/proc/%u/task", pid);
898 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
900 kcount = TT.kcount-kcount+1;
901 tb = (void *)new->extra;
902 tb->slot[SLOT_tcount] = kcount;
904 // Fill out tid and thread count for each entry in group
905 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
906 tb = (void *)dt->extra;
907 tb->slot[SLOT_pid] = pid;
908 tb->slot[SLOT_tcount] = kcount;
912 if (!TT.show_process) return DIRTREE_SAVE;
913 TT.show_process((void *)new->extra);
917 new = dt->child->next;
918 TT.show_process((void *)dt->child->extra);
927 static char *parse_ko(void *data, char *type, int length)
929 struct strawberry *field;
930 char *width, *title, *end, *s;
933 // Get title, length of title, type, end of type, and display width
935 // Chip off =name to display
936 if ((end = strchr(type, '=')) && length>(end-type)) {
938 length -= (end-type)+1;
944 // Chip off :width to display
945 if ((width = strchr(type, ':')) && width<end) {
946 if (!title) length = width-type;
949 // Allocate structure, copy title
950 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
952 memcpy(field->title = field->forever, title, length);
953 field->title[field->len = length] = 0;
957 field->len = strtol(++width, &title, 10);
958 if (!isdigit(*width) || title != end) return title;
964 if (*type == '-') field->reverse = -1;
965 else if (*type != '+') type--;
967 for (i = 0; i<ARRAY_LEN(typos); i++) {
969 for (j = 0; j<2; j++) {
970 if (!j) s = typos[i].name;
971 // posix requires alternate names for some fields
972 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
973 PS_VSZ, PS_USER, 0}, i))) continue;
975 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
977 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
981 if (i==ARRAY_LEN(typos)) return type;
982 if (!field->title) field->title = typos[field->which].name;
983 if (!field->len) field->len = typos[field->which].width;
984 else if (typos[field->which].width<0) field->len *= -1;
985 dlist_add_nomalloc(data, (void *)field);
990 static long long get_headers(struct strawberry *fields, char *buf, int blen)
995 for (; fields; fields = fields->next) {
996 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
998 bits |= 1LL<<fields->which;
1004 // Parse -p -s -t -u -U -g -G
1005 static char *parse_rest(void *data, char *str, int len)
1007 struct ptr_len *pl = (struct ptr_len *)data;
1012 // Allocate next chunk of data
1014 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1016 // Parse numerical input
1017 if (isdigit(*str)) {
1018 ll[pl->len] = xstrtol(str, &end, 10);
1019 if (end==(len+str)) num++;
1020 // For pkill, -s 0 represents pkill's session id.
1021 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1024 if (pl==&TT.pp || pl==&TT.ss) {
1025 if (num && ll[pl->len]>0) {
1030 } else if (pl==&TT.tt) {
1031 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1033 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1034 if (strstart(&str, "pts/")) {
1037 } else if (strstart(&str, "tty")) len -= 3;
1039 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1042 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1043 memcpy(end, str, len);
1046 ll[pl->len++] = st.st_rdev;
1050 } else if (len<255) {
1059 memcpy(name, str, len);
1061 if (pl==&TT.gg || pl==&TT.GG) {
1062 struct group *gr = getgrnam(name);
1064 ll[pl->len++] = gr->gr_gid;
1068 } else if (pl==&TT.uu || pl==&TT.UU) {
1069 struct passwd *pw = getpwnam(name);
1071 ll[pl->len++] = pw->pw_uid;
1083 static int ksort(void *aa, void *bb)
1085 struct strawberry *field;
1086 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1089 for (field = TT.kfields; field && !ret; field = field->next) {
1090 slot = typos[field->which].slot;
1092 // Can we do numeric sort?
1094 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1095 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1098 // fallback to string sort
1100 memccpy(toybuf, string_field(ta, field), 0, 2048);
1102 ret = strcmp(toybuf, string_field(tb, field));
1104 ret *= field->reverse;
1110 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1113 struct dirtree *next = dt->next;
1115 if (dt->extra) *(tb++) = (void *)dt->extra;
1116 if (dt->child) tb = collate_leaves(tb, dt->child);
1124 static struct carveup **collate(int count, struct dirtree *dt)
1126 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1128 collate_leaves(tbsort, dt);
1133 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1135 struct arg_list def;
1137 memset(&def, 0, sizeof(struct arg_list));
1139 comma_args(arg ? arg : &def, fields, err, parse_ko);
1142 static void shared_main(void)
1146 TT.ticks = sysconf(_SC_CLK_TCK);
1150 // If ps can't query terminal size pad to 80 but do -w
1151 if (!terminal_size(&TT.width, &TT.height) && toys.which->name[1] == 's')
1152 toys.optflags |= FLAG_w;
1155 // find controlling tty, falling back to /dev/tty if none
1156 for (i = 0; !TT.tty && i<4; i++) {
1160 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1162 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1163 if (i==3) close(fd);
1175 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 // It's undocumented, but traditionally extra arguments are extra -p args
1190 for (arg = toys.optargs; *arg; arg++)
1191 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit_raw(*arg);
1193 // Figure out which fields to display
1194 not_o = "%sTTY,TIME,CMD";
1195 if (toys.optflags&FLAG_f)
1196 sprintf(not_o = toybuf+128,
1197 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
1198 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1199 else if (toys.optflags&FLAG_l)
1200 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1201 else if (CFG_TOYBOX_ON_ANDROID)
1202 sprintf(not_o = toybuf+128,
1203 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1204 (toys.optflags&FLAG_T) ? "CMD" : "NAME");
1205 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1207 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1208 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1209 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1212 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1213 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1214 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1216 dlist_terminate(TT.fields);
1218 // -f and -n change the meaning of some fields
1219 if (toys.optflags&(FLAG_f|FLAG_n)) {
1220 struct strawberry *ever;
1222 for (ever = TT.fields; ever; ever = ever->next) {
1223 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1224 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1229 // Calculate seen fields bit array, and if we aren't deferring printing
1230 // print headers now (for low memory/nommu systems).
1231 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1232 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1233 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = show_ps;
1234 TT.match_process = ps_match_process;
1235 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1236 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1237 ? get_threads : get_ps);
1239 if (toys.optflags&(FLAG_k|FLAG_M)) {
1240 struct carveup **tbsort = collate(TT.kcount, dt);
1242 if (toys.optflags&FLAG_M) {
1243 for (i = 0; i<TT.kcount; i++) {
1244 struct strawberry *field;
1246 for (field = TT.fields; field; field = field->next) {
1247 int len = strlen(string_field(tbsort[i], field));
1249 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1253 // Now that we've recalculated field widths, re-pad headers again
1254 get_headers(TT.fields, toybuf, sizeof(toybuf));
1255 printf("%.*s\n", TT.width, toybuf);
1258 if (toys.optflags&FLAG_k)
1259 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1260 for (i = 0; i<TT.kcount; i++) {
1264 if (CFG_TOYBOX_FREE) free(tbsort);
1267 if (CFG_TOYBOX_FREE) {
1276 llist_traverse(TT.fields, free);
1282 #include "generated/flags.h"
1284 // select which of the -o fields to sort by
1285 static void setsort(int pos)
1287 struct strawberry *field, *going2;
1292 for (field = TT.fields; field; field = field->next) {
1293 if ((TT.sortpos = i++)<pos && field->next) continue;
1294 going2 = TT.kfields;
1295 going2->which = field->which;
1296 going2->len = field->len;
1301 // If we have both, adjust slot[deltas[]] to be relative to previous
1302 // measurement rather than process start. Stomping old.data is fine
1303 // because we free it after displaying.
1304 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1306 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1307 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1310 for (i = 0; i<ARRAY_LEN(deltas); i++)
1311 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1312 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1317 static int header_line(int line, int rev)
1319 if (!line) return 0;
1321 if (toys.optflags&FLAG_b) rev = 0;
1323 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1324 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1325 rev ? "\033[0m" : "");
1330 static long long millitime(void)
1334 clock_gettime(CLOCK_MONOTONIC, &ts);
1335 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1338 static void top_common(
1339 int (*filter)(long long *oslot, long long *nslot, int milis))
1341 long long timeout = 0, now, stats[16];
1343 struct carveup **tb;
1346 } plist[2], *plold, *plnew, old, new, mix;
1347 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1348 "iow", "irq", "sirq", "host"};
1351 int i, lines, topoff = 0, done = 0;
1353 toys.signal = SIGWINCH;
1354 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1356 memset(plist, 0, sizeof(plist));
1357 memset(stats, 0, sizeof(stats));
1362 plold = plist+(tock++&1);
1363 plnew = plist+(tock&1);
1364 plnew->whence = millitime();
1365 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1366 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1367 ? get_threads : get_ps);
1368 plnew->tb = collate(plnew->count = TT.kcount, dt);
1371 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1372 long long *st = stats+8*(tock&1);
1374 // user nice system idle iowait irq softirq host
1375 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1376 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1379 // First time, wait a quarter of a second to collect a little delta data.
1385 // Collate old and new into "mix", depends on /proc read in pid sort order
1388 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1391 while (old.count || new.count) {
1392 struct carveup *otb = *old.tb, *ntb = *new.tb;
1394 // If we just have old for this process, it exited. Discard it.
1395 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1402 // If we just have new, use it verbatim
1403 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1406 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1407 mix.tb[mix.count] = otb;
1417 // Don't re-fetch data if it's not time yet, just re-display existing data.
1422 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1423 if (!(toys.optflags&FLAG_b)) {
1424 printf("\033[H\033[J");
1427 terminal_probesize(&TT.width, &TT.height);
1432 if (recalc && !(toys.optflags&FLAG_q)) {
1433 // Display "top" header.
1434 if (*toys.which->name == 't') {
1435 struct strawberry alluc;
1436 long long ll, up = 0;
1440 // Count running, sleeping, stopped, zombie processes.
1442 memset(run, 0, sizeof(run));
1443 for (i = 0; i<mix.count; i++)
1444 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1446 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1447 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1448 lines = header_line(lines, 0);
1450 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1451 for (i=0; i<6; i++) {
1452 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1453 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1454 run[i] = pos ? atol(pos) : 0;
1457 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1458 run[0], run[0]-run[1], run[1], run[2]);
1459 lines = header_line(lines, 0);
1461 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1462 run[4], run[4]-run[5], run[5], run[3]);
1463 lines = header_line(lines, 0);
1467 i = sysconf(_SC_NPROCESSORS_CONF);
1468 pos += sprintf(pos, "%d%%cpu", i*100);
1471 // If a processor goes idle it's powered down and its idle ticks don't
1472 // advance, so calculate idle time as potential time - used.
1473 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1476 ll = stats[3] = stats[11] = 0;
1477 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1478 stats[3] = now - llabs(ll);
1480 for (i = 0; i<8; i++) {
1481 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1482 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1484 lines = header_line(lines, 0);
1486 struct strawberry *fields;
1489 memset(&tb, 0, sizeof(struct carveup));
1490 pos = stpcpy(toybuf, "Totals:");
1491 for (fields = TT.fields; fields; fields = fields->next) {
1492 long long ll, bits = 0;
1493 int slot = typos[fields->which].slot&63;
1495 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1496 ll = 1LL<<fields->which;
1497 if (bits&ll) continue;
1499 for (i=0; i<mix.count; i++)
1500 tb.slot[slot] += mix.tb[i]->slot[slot];
1501 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1502 " %s: %*s,", typos[fields->which].name,
1503 fields->len, string_field(&tb, fields));
1506 lines = header_line(lines, 0);
1509 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1510 for (i = 0, is = ' '; *pos; pos++) {
1513 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1515 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1518 lines = header_line(lines, 1);
1520 if (!recalc && !(toys.optflags&FLAG_b))
1521 printf("\033[%dH\033[J", 1+TT.height-lines);
1524 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1525 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1526 show_ps(mix.tb[i+topoff]);
1529 if (TT.top.n && !--TT.top.n) {
1535 if (timeout<=now) timeout = new.whence+TT.top.d;
1536 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1538 // In batch mode, we ignore the keyboard.
1539 if (toys.optflags&FLAG_b) {
1540 msleep(timeout-now);
1541 // Make an obvious gap between datasets.
1546 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1547 if (i==-1 || i==3 || toupper(i)=='Q') {
1552 if (i==-3) continue;
1554 // Flush unknown escape sequences.
1555 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1559 } else if (toupper(i)=='R')
1560 ((struct strawberry *)TT.kfields)->reverse *= -1;
1563 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1564 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1565 // KEY_UP is 0, so at end of strchr
1566 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1569 if (i == KEY_UP) topoff--;
1570 else if (i == KEY_DOWN) topoff++;
1571 else if (i == KEY_PGDN) topoff += lines;
1572 else if (i == KEY_PGUP) topoff -= lines;
1573 if (topoff<0) topoff = 0;
1574 if (topoff>mix.count) topoff = mix.count;
1581 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1585 if (!(toys.optflags&FLAG_b)) tty_reset();
1588 static void top_setup(char *defo, char *defk)
1591 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1593 TT.time = millitime();
1594 set_terminal(0, 1, 0);
1595 sigatexit(tty_sigreset);
1596 xsignal(SIGWINCH, generic_signal);
1597 printf("\033[?25l\033[0m");
1601 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1602 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1603 TT.match_process = shared_match_process;
1605 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1606 dlist_terminate(TT.fields);
1608 // First (dummy) sort field is overwritten by setsort()
1609 default_ko("-S", &TT.kfields, 0, 0);
1610 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1611 dlist_terminate(TT.kfields);
1612 setsort(TT.top.s-1);
1617 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,%s",
1618 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1619 toys.optflags&FLAG_H ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1620 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1621 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1623 struct strawberry *fields = TT.fields;
1625 fields = fields->next->next;
1626 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1629 top_common(merge_deltas);
1634 #include "generated/flags.h"
1636 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1638 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1639 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1641 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1644 void iotop_main(void)
1646 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1648 if (toys.optflags&FLAG_K) TT.forcek++;
1650 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1651 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1654 top_common(iotop_filter);
1657 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1658 // context, so force pgrep's flags on even when building pkill standalone.
1659 // (All the pgrep/pkill functions drop out when building ps standalone.)
1661 #define CLEANUP_iotop
1663 #include "generated/flags.h"
1666 struct regex_list *next;
1670 static void do_pgk(struct carveup *tb)
1672 if (TT.pgrep.signal) {
1673 if (kill(*tb->slot, TT.pgrep.signal)) {
1674 char *s = num_to_sig(TT.pgrep.signal);
1676 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1677 perror_msg("%s->%lld", s, *tb->slot);
1680 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1681 printf("%lld", *tb->slot);
1682 if (toys.optflags&FLAG_l)
1683 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1685 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1689 static void match_pgrep(void *p)
1691 struct carveup *tb = p;
1693 struct regex_list *reg;
1694 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1696 // Never match ourselves.
1697 if (TT.pgrep.self == *tb->slot) return;
1699 if (TT.pgrep.regexes) {
1700 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1701 if (regexec(®->reg, name, 1, &match, 0)) continue;
1702 if (toys.optflags&FLAG_x)
1703 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1706 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1709 // pgrep should return success if there's a match.
1712 // Repurpose a field for -c count.
1714 if (toys.optflags&(FLAG_n|FLAG_o)) {
1715 long long ll = tb->slot[SLOT_starttime];
1717 if (toys.optflags&FLAG_o) ll *= -1;
1718 if (TT.time && TT.time>ll) return;
1720 free(TT.pgrep.snapshot);
1721 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1725 static int pgrep_match_process(long long *slot)
1727 int match = shared_match_process(slot);
1729 return (toys.optflags&FLAG_v) ? !match : match;
1732 void pgrep_main(void)
1735 struct regex_list *reg;
1737 TT.pgrep.self = getpid();
1739 // No signal names start with "L", so no need for "L: " parsing.
1740 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1741 error_exit("bad -L '%s'", TT.pgrep.L);
1743 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1744 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1745 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1746 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1747 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1748 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1749 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1751 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1752 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1753 if (!toys.optc) help_exit("No PATTERN");
1755 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1756 for (arg = toys.optargs; *arg; arg++) {
1757 reg = xmalloc(sizeof(struct regex_list));
1758 xregcomp(®->reg, *arg, REG_EXTENDED);
1759 reg->next = TT.pgrep.regexes;
1760 TT.pgrep.regexes = reg;
1762 TT.match_process = pgrep_match_process;
1763 TT.show_process = match_pgrep;
1765 // pgrep should return failure if there are no matches.
1768 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1769 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1770 if (TT.pgrep.snapshot) {
1771 do_pgk(TT.pgrep.snapshot);
1772 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1774 if (TT.pgrep.d) xputc('\n');
1777 #define CLEANUP_pgrep
1779 #include "generated/flags.h"
1781 void pkill_main(void)
1783 char **args = toys.optargs;
1785 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1786 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1787 if (toys.optflags & FLAG_V) TT.tty = 1;