1 /* ps.c - show process list
3 * Copyright 2015 Rob Landley <rob@landley.net>
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
6 * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
7 * And linux kernel source fs/proc/array.c function do_task_stat()
9 * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to
10 * mean "show numeric users and groups" instead.
11 * Posix says default output should have field named "TTY" but if you "-o tty"
12 * the same field should be called "TT" which is _INSANE_ and I'm not doing it.
13 * Similarly -f outputs USER but calls it UID (we call it USER).
14 * It also says that -o "args" and "comm" should behave differently but use
15 * the same title, which is not the same title as the default output. (No.)
16 * Select by session id is -s not -g.
18 * Posix defines -o ADDR as "The address of the process" but the process
19 * start address is a constant on any elf system with mmu. The procps ADDR
20 * field always prints "-" with an alignment of 1, which is why it has 11
21 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
22 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
23 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
24 * which changes -l by removing the "F" column and swapping RSS for ADDR,
25 * leaving 9 chars for cmd, so we're using that as our -l output.
27 * Added a bunch of new -o fields posix doesn't mention, and we don't
28 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
29 * output argv[0] unmodified for -o comm or -o args (but procps violates
30 * posix for -o comm anyway, it's stat[2] not argv[0]).
32 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
33 * files (why they're not globally readable when the rest of proc
34 * data is...?) and get a global I/O picture. Normal top is NOT,
35 * even though you can -o AIO there, to give sysadmins the option
36 * to reduce security exposure.)
38 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
39 * TODO: switch -fl to -y
40 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
41 * TODO: iotop: Window size change: respond immediately. Why not padding
42 * at right edge? (Not adjusting to screen size at all? Header wraps?)
43 * TODO: top: thread support and SMP
44 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
46 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
47 // stayroot because iotop needs root to read other process' proc/$$/io
48 USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
49 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
50 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
51 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
57 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
61 Which processes to show (selections may be comma separated lists):
64 -a Processes with terminals that aren't session leaders
65 -d All processes that aren't session leaders
67 -g Belonging to GROUPs
68 -G Belonging to real GROUPs (before sgid)
70 -P Parent PIDs (--ppid)
72 -t Attached to selected TTYs
75 -U Owned by real USERs (before suid)
79 -k Sort FIELDs in +increasing or -decreasting order (--sort)
80 -M Measure field widths (expanding as necessary)
81 -n Show numeric USER and GROUP
82 -w Wide output (don't truncate at terminal width)
84 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
86 -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
87 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
88 -o Output FIELDs instead of defaults, each with optional :size and =title
89 -O Add FIELDS to defaults
92 Command line -o fields:
94 ARGS Command line (argv[] -path) CMD COMM, or ARGS with -f
95 CMDLINE Command line (argv[]) COMM Original command name (stat[2])
96 COMMAND Command name (/proc/$PID/exe) NAME Command name (COMMAND -path)
97 TNAME Thread name (argv[0] of $PID)
99 Process attribute -o FIELDs:
101 ADDR Instruction pointer BIT Is this process 32 or 64 bits
102 CPU Which processor running on ETIME Elapsed time since PID start
103 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
104 GROUP Group name LABEL Security label
105 MAJFL Major page faults MINFL Minor page faults
106 NI Niceness (lower is faster)
107 PCPU Percentage of CPU time used PCY Android scheduling policy
108 PGID Process Group ID
109 PID Process ID PPID Parent Process ID
110 PRI Priority (higher is faster) PSR Processor last executed on
111 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
112 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
113 RUID Real (before suid) user ID RUSER Real (before suid) user name
115 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
116 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
117 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
118 STAT Process state (S) plus:
119 < high priority N low priority L locked memory
120 s session leader + foreground l multithreaded
121 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
122 SZ Memory Size (4k pages needed to completely swap out process)
123 TCNT Thread count TID Thread ID
124 TIME CPU time consumed TTY Controlling terminal
125 UID User id USER User name
126 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
127 WCHAN What are we waiting in kernel for
131 depends on TOP_COMMON
134 usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
136 Show process activity in real time.
139 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
140 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
141 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
142 -s Sort by field number (1-X, default 9)
144 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
147 depends on TOP_COMMON
152 Rank processes by I/O.
154 -A All I/O, not just disk
155 -a Accumulated I/O (not percentage)
157 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
158 -O Only show processes doing I/O
159 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
160 -s Sort by field number (0-X, default 6)
166 usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
168 -b Batch mode (no tty)
169 -d Delay SECONDS between each cycle (default 3)
170 -n Exit after NUMBER iterations
173 -q Quiet (no header lines)
175 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
176 update, R to reverse sort, Q to exit.
181 depends on PGKILL_COMMON
183 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
185 Search for process(es). PATTERN is an extended regular expression checked
186 against command names.
188 -c Show only count of matches
189 -d Use DELIM instead of newline
190 -L Send SIGNAL instead of printing name
196 depends on PGKILL_COMMON
198 usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
200 -l Send SIGNAL (default SIGTERM)
207 usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
209 -f Check full command line for PATTERN
210 -G Match real Group ID(s)
211 -g Match Process Group(s) (0 is current user)
214 -P Match Parent Process ID(s)
215 -s Match Session ID(s) (0 for current)
217 -U Match real User ID(s)
218 -u Match effective User ID(s)
220 -x Match whole command (not substring)
262 void *regexes, *snapshot;
269 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
270 struct dirtree *threadparent;
271 unsigned width, height;
273 void *fields, *kfields;
274 long long ticks, bits, time;
275 int kcount, forcek, sortpos;
276 int (*match_process)(long long *slot);
277 void (*show_process)(void *tb);
281 struct strawberry *next, *prev;
282 short which, len, reverse;
287 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
288 * table 1-4) but we shift and repurpose fields, with the result being: */
291 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
292 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
293 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
294 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
295 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
296 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
297 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
298 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
299 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
300 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
301 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
302 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
303 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
304 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
305 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
306 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
307 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
308 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
309 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
310 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
311 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
312 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
313 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
314 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
315 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
316 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
317 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
318 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
319 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
320 SLOT_pcy, /*Android sched policy*/
325 // Data layout in toybuf
327 long long slot[SLOT_count]; // data (see enum above)
328 unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
330 char str[]; // name, tty, command, wchan, attr, cmdline
333 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
334 // 64|slot means compare as string when sorting
337 signed char width, slot;
338 } static const typos[] = TAGGED_ARRAY(PS,
340 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
341 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
342 {"SZ", 5, SLOT_vsize}, {"RSS", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
343 {"VSZ", 6, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
344 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
345 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
346 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
349 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -15, -1},
350 {"NAME", -15, -5}, {"TNAME", -27, -7}, {"COMMAND", -27, -5},
351 {"CMDLINE", -27, -6}, {"ARGS", -27, -6}, {"CMD", -27, -1},
354 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
355 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
356 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
359 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
360 {"TIME+", 9, SLOT_utime},
362 // Percentage displays
363 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
364 {"%CPU", 4, SLOT_utime2},
367 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
368 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
369 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
370 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
373 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
374 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
377 // Return 0 to discard, nonzero to keep
378 static int shared_match_process(long long *slot)
380 struct ptr_len match[] = {
381 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
382 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
383 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
388 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
389 for (i = 0; i < ARRAY_LEN(match); i++) {
390 struct ptr_len *mm = match[i].ptr;
394 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
402 // Return 0 to discard, nonzero to keep
403 static int ps_match_process(long long *slot)
405 int i = shared_match_process(slot);
408 // If we had selections and didn't match them, don't display
411 // Filter implicit categories for other display types
412 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
413 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
414 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
415 && TT.tty!=slot[SLOT_ttynr]) return 0;
420 // Convert field to string representation
421 static char *string_field(struct carveup *tb, struct strawberry *field)
423 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
424 int which = field->which, sl = typos[which].slot;
425 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
427 // numbers, mostly from /proc/$PID/stat
428 if (which <= PS_BIT) {
431 if (which==PS_PRI) ll = 39-ll;
432 if (which==PS_ADDR) fmt = "%llx";
433 else if (which==PS_SZ) ll >>= 12;
434 else if (which==PS_RSS) ll <<= 2;
435 else if (which==PS_VSZ) ll >>= 10;
436 else if (which==PS_PR && ll<-9) fmt="RT";
437 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
438 sprintf(out, fmt, ll);
444 // First string slot has offset 0, others are offset[-slot-2]
445 if (--sl) out += tb->offset[--sl];
446 if (which==PS_ARGS || which==PS_NAME) {
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(struct carveup *tb)
550 struct strawberry *field;
551 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
553 // Loop through fields to display
554 for (field = TT.fields; field; field = field->next) {
555 char *out = string_field(tb, field);
557 // Output the field, appropriately padded
559 // Minimum one space between each field
560 if (field != TT.fields) {
565 // Don't truncate number fields, but try to reclaim extra offset from later
566 // fields that can naturally be shorter
567 abslen = abs(field->len);
568 sign = field->len<0 ? -1 : 1;
569 if (field->which<=PS_BIT || extra) olen = strlen(out);
570 if (field->which<=PS_BIT && olen>abslen) {
571 // overflow but remember by how much
572 extra += olen-abslen;
574 } else if (extra && olen<abslen) {
575 // If later fields have slack space, take back overflow
577 if (olen>extra) olen = extra;
581 if (abslen>width) abslen = width;
584 // If last field is left justified, no trailing spaces.
585 if (!field->next && sign<0) pad = 0;
587 if (TT.tty) width -= draw_trim(out, pad, len);
588 else width -= printf("%*.*s", pad, len, out);
591 xputc(TT.time ? '\r' : '\n');
594 // dirtree callback: read data about process to display, store, or discard it.
595 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
596 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
597 static int get_ps(struct dirtree *new)
600 char *name; // Path under /proc/$PID directory
601 long long bits; // Only fetch extra data if an -o field is displaying it
603 // sources for carveup->offset[] data
604 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
605 {"exe", _PS_COMMAND|_PS_NAME}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_TNAME},
608 struct carveup *tb = (void *)toybuf;
609 long long *slot = tb->slot;
610 char *name, *s, *buf = tb->str, *end = 0;
614 // Recurse one level into /proc children, skip non-numeric entries
616 return DIRTREE_RECURSE|DIRTREE_SHUTUP
617 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
619 memset(slot, 0, sizeof(tb->slot));
620 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
621 if (TT.threadparent && TT.threadparent->extra)
622 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
623 fd = dirtree_parentfd(new);
626 sprintf(buf, "%lld/stat", *slot);
627 if (!readfileat(fd, buf, buf, &len)) return 0;
629 // parse oddball fields (name and state). Name can have embedded ')' so match
630 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
631 // All remaining fields should be numeric.
632 if (!(name = strchr(buf, '('))) return 0;
633 for (s = ++name; *s; s++) if (*s == ')') end = s;
634 if (!end || end-name>255) return 0;
636 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
637 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
638 for (j = 1; j<SLOT_count; j++)
639 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
641 // Now we've read the data, move status and name right after slot[] array,
642 // and convert low chars to ? for non-tty display while we're at it.
643 for (i = 0; i<end-name; i++)
644 if ((tb->str[i] = name[i]) < ' ')
645 if (!TT.tty) tb->str[i] = '?';
648 len = sizeof(toybuf)-(buf-toybuf);
650 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
651 // or numeric wchan, and the remaining two are always zero), and vmlck into
652 // 18 (which is "obsolete, always 0" from stat)
653 slot[SLOT_uid] = new->st.st_uid;
654 slot[SLOT_gid] = new->st.st_gid;
656 // TIME and TIME+ use combined value, ksort needs 'em added.
657 slot[SLOT_utime] += slot[SLOT_stime];
658 slot[SLOT_utime2] = slot[SLOT_utime];
660 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
661 // and save ruid, rgid, and vmlck.
662 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
663 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
667 sprintf(buf, "%lld/status", *slot);
668 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
669 s = strafter(buf, "\nUid:");
670 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
671 s = strafter(buf, "\nGid:");
672 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
673 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
674 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
677 // Do we need to read "io"?
678 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
681 sprintf(buf, "%lld/io", *slot);
682 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
683 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
684 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
685 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
686 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
687 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
688 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
691 // We now know enough to skip processes we don't care about.
692 if (TT.match_process && !TT.match_process(slot)) return 0;
694 // /proc data is generated as it's read, so for maximum accuracy on slow
695 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
697 slot[SLOT_uptime] = TT.si.uptime;
698 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
700 // Do we need to read "statm"?
701 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
704 sprintf(buf, "%lld/statm", *slot);
705 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
707 for (s = buf, i=0; i<3; i++)
708 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
712 // Do we need to read "exe"?
713 if (TT.bits&_PS_BIT) {
716 sprintf(buf, "%lld/exe", *slot);
717 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
718 if (buf[4] == 1) slot[SLOT_bits] = 32;
719 else if (buf[4] == 2) slot[SLOT_bits] = 64;
723 // Do we need Android scheduling policy?
724 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
726 // Fetch string data while parentfd still available, appending to buf.
727 // (There's well over 3k of toybuf left. We could dynamically malloc, but
728 // it'd almost never get used, querying length of a proc file is awkward,
729 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
730 slot[SLOT_argv0len] = 0;
731 for (j = 0; j<ARRAY_LEN(fetch); j++) {
732 tb->offset[j] = buf-(tb->str);
733 if (!(TT.bits&fetch[j].bits)) {
738 // Determine remaining space, reserving minimum of 256 bytes/field and
739 // 260 bytes scratch space at the end (for output conversion later).
740 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
741 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
743 // For exe we readlink instead of read contents
745 struct carveup *ptb = 0;
748 // Thread doesn't have exe or argv[0], so use parent's
749 if (TT.threadparent && TT.threadparent->extra)
750 ptb = (void *)TT.threadparent->extra;
753 if ((len = readlinkat(fd, buf, buf, len))<1) len = 0;
755 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
757 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
758 i = ptb->slot[SLOT_argv0len];
759 s = ptb->str+ptb->offset[4];
760 while (-1!=(k = stridx(s, '/')) && k<i) {
770 // If it's not the TTY field, data we want is in a file.
771 // Last length saved in slot[] is command line (which has embedded NULs)
773 int rdev = slot[SLOT_ttynr];
776 // Call no tty "?" rather than "0:0".
779 // Can we readlink() our way to a name?
780 for (i = 0; i<3; i++) {
781 sprintf(buf, "%lld/fd/%i", *slot, i);
782 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
783 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
790 // Couldn't find it, try all the tty drivers.
792 FILE *fp = fopen("/proc/tty/drivers", "r");
793 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
796 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
797 // TODO: we could parse the minor range too.
798 if (tty_major == maj) {
800 len += sprintf(buf+len, "%d", min);
801 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
809 // Really couldn't find it, so just show major:minor.
810 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
814 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
817 // Data we want is in a file.
818 // Last length saved in slot[] is command line (which has embedded NULs)
821 // When command has no arguments, don't space over the NUL
822 if (readfileat(fd, buf, buf, &len) && len>0) {
825 // Trim trailing whitespace and NUL bytes
827 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
830 // Turn NUL to space, other low ascii to ? (in non-tty mode)
831 // cmdline has a trailing NUL that we don't want to turn to space.
832 for (i=0; i<len-1; i++) {
838 } else if (!TT.tty && c<' ') c = '?';
841 // Store end of argv[0] so ARGS and CMDLINE can differ.
842 // We do it for each file string slot but last is cmdline, which sticks.
843 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
844 } else *buf = len = 0;
847 // Above calculated/retained len, so we don't need to re-strlen.
852 if (TT.show_process && !TT.threadparent) {
858 // If we need to sort the output, add it to the list and return.
859 s = xmalloc(buf-toybuf);
860 new->extra = (long)s;
861 memcpy(s, toybuf, buf-toybuf);
866 static int get_threads(struct dirtree *new)
870 unsigned pid, kcount;
872 if (!new->parent) return get_ps(new);
874 if (!(pid = atol(new->name))) return 0;
876 TT.threadparent = new;
883 // Recurse down into tasks, retaining thread groups.
884 // Disable show_process at least until we can calculate tcount
886 sprintf(toybuf, "/proc/%u/task", pid);
887 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
889 kcount = TT.kcount-kcount+1;
890 tb = (void *)new->extra;
891 tb->slot[SLOT_tcount] = kcount;
893 // Fill out tid and thread count for each entry in group
894 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
895 tb = (void *)dt->extra;
896 tb->slot[SLOT_pid] = pid;
897 tb->slot[SLOT_tcount] = kcount;
901 if (!TT.show_process) return DIRTREE_SAVE;
902 TT.show_process((void *)new->extra);
906 new = dt->child->next;
907 TT.show_process((void *)dt->child->extra);
916 static char *parse_ko(void *data, char *type, int length)
918 struct strawberry *field;
919 char *width, *title, *end, *s;
922 // Get title, length of title, type, end of type, and display width
924 // Chip off =name to display
925 if ((end = strchr(type, '=')) && length>(end-type)) {
927 length -= (end-type)+1;
933 // Chip off :width to display
934 if ((width = strchr(type, ':')) && width<end) {
935 if (!title) length = width-type;
938 // Allocate structure, copy title
939 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
941 memcpy(field->title = field->forever, title, length);
942 field->title[field->len = length] = 0;
946 field->len = strtol(++width, &title, 10);
947 if (!isdigit(*width) || title != end) return title;
953 if (*type == '-') field->reverse = -1;
954 else if (*type != '+') type--;
956 for (i = 0; i<ARRAY_LEN(typos); i++) {
958 for (j = 0; j<2; j++) {
959 if (!j) s = typos[i].name;
960 // posix requires alternate names for some fields
961 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
962 PS_VSZ, PS_USER, 0}, i))) continue;
964 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
966 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
970 if (i==ARRAY_LEN(typos)) return type;
971 if (!field->title) field->title = typos[field->which].name;
972 if (!field->len) field->len = typos[field->which].width;
973 else if (typos[field->which].width<0) field->len *= -1;
974 dlist_add_nomalloc(data, (void *)field);
979 long long get_headers(struct strawberry *fields, char *buf, int blen)
984 for (; fields; fields = fields->next) {
985 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
987 bits |= 1LL<<fields->which;
993 // Parse -p -s -t -u -U -g -G
994 static char *parse_rest(void *data, char *str, int len)
996 struct ptr_len *pl = (struct ptr_len *)data;
1001 // Allocate next chunk of data
1003 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1005 // Parse numerical input
1006 if (isdigit(*str)) {
1007 ll[pl->len] = xstrtol(str, &end, 10);
1008 if (end==(len+str)) num++;
1009 // For pkill, -s 0 represents pkill's session id.
1010 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1013 if (pl==&TT.pp || pl==&TT.ss) {
1014 if (num && ll[pl->len]>0) {
1019 } else if (pl==&TT.tt) {
1020 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1022 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1023 if (strstart(&str, "pts/")) {
1026 } else if (strstart(&str, "tty")) len -= 3;
1028 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1031 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1032 memcpy(end, str, len);
1035 ll[pl->len++] = st.st_rdev;
1039 } else if (len<255) {
1048 memcpy(name, str, len);
1050 if (pl==&TT.gg || pl==&TT.GG) {
1051 struct group *gr = getgrnam(name);
1053 ll[pl->len++] = gr->gr_gid;
1057 } else if (pl==&TT.uu || pl==&TT.UU) {
1058 struct passwd *pw = getpwnam(name);
1060 ll[pl->len++] = pw->pw_uid;
1072 static int ksort(void *aa, void *bb)
1074 struct strawberry *field;
1075 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1078 for (field = TT.kfields; field && !ret; field = field->next) {
1079 slot = typos[field->which].slot;
1081 // Can we do numeric sort?
1083 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1084 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1087 // fallback to string sort
1089 memccpy(toybuf, string_field(ta, field), 0, 2048);
1091 ret = strcmp(toybuf, string_field(tb, field));
1093 ret *= field->reverse;
1099 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1102 struct dirtree *next = dt->next;
1104 if (dt->extra) *(tb++) = (void *)dt->extra;
1105 if (dt->child) tb = collate_leaves(tb, dt->child);
1113 static struct carveup **collate(int count, struct dirtree *dt)
1115 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1117 collate_leaves(tbsort, dt);
1122 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1124 struct arg_list def;
1126 memset(&def, 0, sizeof(struct arg_list));
1128 comma_args(arg ? arg : &def, fields, err, parse_ko);
1131 static void shared_main(void)
1135 TT.ticks = sysconf(_SC_CLK_TCK);
1137 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1139 terminal_size(&TT.width, &TT.height);
1142 // find controlling tty, falling back to /dev/tty if none
1143 for (i = 0; !TT.tty && i<4; i++) {
1147 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1149 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1150 if (i==3) close(fd);
1160 if (toys.optflags&FLAG_w) TT.width = 99999;
1163 // parse command line options other than -o
1164 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1165 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1166 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1167 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1168 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1169 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1170 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1171 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1172 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1173 dlist_terminate(TT.kfields);
1175 // Figure out which fields to display
1176 not_o = "%sTTY,TIME,CMD";
1177 if (toys.optflags&FLAG_f)
1178 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,CMD",
1179 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1180 else if (toys.optflags&FLAG_l)
1181 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1182 else if (CFG_TOYBOX_ON_ANDROID)
1183 not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1184 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1186 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1187 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1188 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1191 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1192 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1193 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1195 dlist_terminate(TT.fields);
1197 // -f and -n change the meaning of some fields
1198 if (toys.optflags&(FLAG_f|FLAG_n)) {
1199 struct strawberry *ever;
1201 for (ever = TT.fields; ever; ever = ever->next) {
1202 if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1203 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1204 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1209 // Calculate seen fields bit array, and if we aren't deferring printing
1210 // print headers now (for low memory/nommu systems).
1211 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1212 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1213 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1214 TT.match_process = ps_match_process;
1215 dt = dirtree_read("/proc",
1216 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT|_PS_TNAME)))
1217 ? get_threads : get_ps);
1219 if (toys.optflags&(FLAG_k|FLAG_M)) {
1220 struct carveup **tbsort = collate(TT.kcount, dt);
1222 if (toys.optflags&FLAG_M) {
1223 for (i = 0; i<TT.kcount; i++) {
1224 struct strawberry *field;
1226 for (field = TT.fields; field; field = field->next) {
1227 int len = strlen(string_field(tbsort[i], field));
1229 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1233 // Now that we've recalculated field widths, re-pad headers again
1234 get_headers(TT.fields, toybuf, sizeof(toybuf));
1235 printf("%.*s\n", TT.width, toybuf);
1238 if (toys.optflags&FLAG_k)
1239 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1240 for (i = 0; i<TT.kcount; i++) {
1244 if (CFG_TOYBOX_FREE) free(tbsort);
1247 if (CFG_TOYBOX_FREE) {
1256 llist_traverse(TT.fields, free);
1262 #include "generated/flags.h"
1264 // select which of the -o fields to sort by
1265 static void setsort(int pos)
1267 struct strawberry *field, *going2;
1272 for (field = TT.fields; field; field = field->next) {
1273 if ((TT.sortpos = i++)<pos && field->next) continue;
1274 going2 = TT.kfields;
1275 going2->which = field->which;
1276 going2->len = field->len;
1281 // If we have both, adjust slot[deltas[]] to be relative to previous
1282 // measurement rather than process start. Stomping old.data is fine
1283 // because we free it after displaying.
1284 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1286 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1287 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1290 for (i = 0; i<ARRAY_LEN(deltas); i++)
1291 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1292 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1297 static int header_line(int line, int rev)
1299 if (!line) return 0;
1301 if (toys.optflags&FLAG_b) rev = 0;
1303 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1304 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1305 rev ? "\033[0m" : "");
1310 static long long millitime(void)
1314 clock_gettime(CLOCK_MONOTONIC, &ts);
1315 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1318 static void top_common(
1319 int (*filter)(long long *oslot, long long *nslot, int milis))
1321 long long timeout = 0, now, stats[16];
1323 struct carveup **tb;
1326 } plist[2], *plold, *plnew, old, new, mix;
1327 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1328 "iow", "irq", "sirq", "host"};
1331 int i, lines, topoff = 0, done = 0;
1333 toys.signal = SIGWINCH;
1334 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1336 memset(plist, 0, sizeof(plist));
1337 memset(stats, 0, sizeof(stats));
1342 plold = plist+(tock++&1);
1343 plnew = plist+(tock&1);
1344 plnew->whence = millitime();
1345 dt = dirtree_read("/proc",
1346 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1347 ? get_threads : get_ps);
1348 plnew->tb = collate(plnew->count = TT.kcount, dt);
1351 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1352 long long *st = stats+8*(tock&1);
1354 // user nice system idle iowait irq softirq host
1355 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1356 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1359 // First time, wait a quarter of a second to collect a little delta data.
1365 // Collate old and new into "mix", depends on /proc read in pid sort order
1368 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1371 while (old.count || new.count) {
1372 struct carveup *otb = *old.tb, *ntb = *new.tb;
1374 // If we just have old for this process, it exited. Discard it.
1375 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1382 // If we just have new, use it verbatim
1383 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1386 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1387 mix.tb[mix.count] = otb;
1397 // Don't re-fetch data if it's not time yet, just re-display existing data.
1402 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1403 if (!(toys.optflags&FLAG_b)) {
1404 printf("\033[H\033[J");
1407 terminal_probesize(&TT.width, &TT.height);
1412 if (recalc && !(toys.optflags&FLAG_q)) {
1413 // Display "top" header.
1414 if (*toys.which->name == 't') {
1415 struct strawberry alluc;
1416 long long ll, up = 0;
1420 // Count running, sleeping, stopped, zombie processes.
1422 memset(run, 0, sizeof(run));
1423 for (i = 0; i<mix.count; i++)
1424 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1426 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1427 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1428 lines = header_line(lines, 0);
1430 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1431 for (i=0; i<6; i++) {
1432 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1433 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1434 run[i] = pos ? atol(pos) : 0;
1437 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1438 run[0], run[0]-run[1], run[1], run[2]);
1439 lines = header_line(lines, 0);
1441 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1442 run[4], run[4]-run[5], run[5], run[3]);
1443 lines = header_line(lines, 0);
1447 i = sysconf(_SC_NPROCESSORS_CONF);
1448 pos += sprintf(pos, "%d%%cpu", i*100);
1451 // If a processor goes idle it's powered down and its idle ticks don't
1452 // advance, so calculate idle time as potential time - used.
1453 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1456 ll = stats[3] = stats[11] = 0;
1457 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1458 stats[3] = now - llabs(ll);
1460 for (i = 0; i<8; i++) {
1461 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1462 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1464 lines = header_line(lines, 0);
1466 struct strawberry *fields;
1469 memset(&tb, 0, sizeof(struct carveup));
1470 pos = stpcpy(toybuf, "Totals:");
1471 for (fields = TT.fields; fields; fields = fields->next) {
1472 long long ll, bits = 0;
1473 int slot = typos[fields->which].slot&63;
1475 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1476 ll = 1LL<<fields->which;
1477 if (bits&ll) continue;
1479 for (i=0; i<mix.count; i++)
1480 tb.slot[slot] += mix.tb[i]->slot[slot];
1481 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1482 " %s: %*s,", typos[fields->which].name,
1483 fields->len, string_field(&tb, fields));
1486 lines = header_line(lines, 0);
1489 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1490 for (i = 0, is = ' '; *pos; pos++) {
1493 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1495 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1498 lines = header_line(lines, 1);
1500 if (!recalc && !(toys.optflags&FLAG_b))
1501 printf("\033[%dH\033[J", 1+TT.height-lines);
1504 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1505 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1506 show_ps(mix.tb[i+topoff]);
1509 if (TT.top.n && !--TT.top.n) {
1515 if (timeout<=now) timeout = new.whence+TT.top.d;
1516 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1518 // In batch mode, we ignore the keyboard.
1519 if (toys.optflags&FLAG_b) {
1520 msleep(timeout-now);
1521 // Make an obvious gap between datasets.
1526 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1527 if (i==-1 || i==3 || toupper(i)=='Q') {
1533 // Flush unknown escape sequences.
1534 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1538 } else if (toupper(i)=='R')
1539 ((struct strawberry *)TT.kfields)->reverse *= -1;
1542 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1543 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1544 // KEY_UP is 0, so at end of strchr
1545 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1548 if (i == KEY_UP) topoff--;
1549 else if (i == KEY_DOWN) topoff++;
1550 else if (i == KEY_PGDN) topoff += lines;
1551 else if (i == KEY_PGUP) topoff -= lines;
1552 if (topoff<0) topoff = 0;
1553 if (topoff>mix.count) topoff = mix.count;
1560 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1564 if (!(toys.optflags&FLAG_b)) tty_reset();
1567 static void top_setup(char *defo, char *defk)
1570 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1572 TT.time = millitime();
1573 set_terminal(0, 1, 0);
1574 sigatexit(tty_sigreset);
1575 xsignal(SIGWINCH, generic_signal);
1576 printf("\033[?25l\033[0m");
1580 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1581 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1582 TT.match_process = shared_match_process;
1584 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1585 dlist_terminate(TT.fields);
1587 // First (dummy) sort field is overwritten by setsort()
1588 default_ko("-S", &TT.kfields, 0, 0);
1589 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1590 dlist_terminate(TT.kfields);
1591 setsort(TT.top.s-1);
1596 // usage: [-h HEADER] -o OUTPUT -k SORT
1598 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1599 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1600 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1601 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1603 struct strawberry *fields = TT.fields;
1605 fields = fields->next->next;
1606 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1609 top_common(merge_deltas);
1614 #include "generated/flags.h"
1616 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1618 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1619 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1621 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1624 void iotop_main(void)
1626 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1628 if (toys.optflags&FLAG_K) TT.forcek++;
1630 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1631 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1634 top_common(iotop_filter);
1637 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1638 // context, so force pgrep's flags on even when building pkill standalone.
1639 // (All the pgrep/pkill functions drop out when building ps standalone.)
1641 #define CLEANUP_iotop
1643 #include "generated/flags.h"
1646 struct regex_list *next;
1650 static void do_pgk(struct carveup *tb)
1652 if (TT.pgrep.signal) {
1653 if (kill(*tb->slot, TT.pgrep.signal)) {
1654 char *s = num_to_sig(TT.pgrep.signal);
1656 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1657 perror_msg("%s->%lld", s, *tb->slot);
1660 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1661 printf("%lld", *tb->slot);
1662 if (toys.optflags&FLAG_l)
1663 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1665 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1669 static void match_pgrep(struct carveup *tb)
1672 struct regex_list *reg;
1673 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1675 // Never match ourselves.
1676 if (TT.pgrep.self == *tb->slot) return;
1678 if (TT.pgrep.regexes) {
1679 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1680 if (regexec(®->reg, name, 1, &match, 0)) continue;
1681 if (toys.optflags&FLAG_x)
1682 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1685 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1688 // Repurpose a field for -c count
1690 if (toys.optflags&(FLAG_n|FLAG_o)) {
1691 long long ll = tb->slot[SLOT_starttime];
1693 if (toys.optflags&FLAG_o) ll *= -1;
1694 if (TT.time && TT.time>ll) return;
1696 free(TT.pgrep.snapshot);
1697 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1701 static int pgrep_match_process(long long *slot)
1703 int match = shared_match_process(slot);
1705 return (toys.optflags&FLAG_v) ? !match : match;
1708 void pgrep_main(void)
1711 struct regex_list *reg;
1713 TT.pgrep.self = getpid();
1715 // No signal names start with "L", so no need for "L: " parsing.
1716 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1717 error_exit("bad -L '%s'", TT.pgrep.L);
1719 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1720 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1721 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1722 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1723 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1724 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1725 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1727 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1728 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1729 if (!toys.optc) help_exit("No PATTERN");
1731 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1732 for (arg = toys.optargs; *arg; arg++) {
1733 reg = xmalloc(sizeof(struct regex_list));
1734 xregcomp(®->reg, *arg, REG_EXTENDED);
1735 reg->next = TT.pgrep.regexes;
1736 TT.pgrep.regexes = reg;
1738 TT.match_process = pgrep_match_process;
1739 TT.show_process = (void *)match_pgrep;
1741 dirtree_read("/proc", get_ps);
1742 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1743 if (TT.pgrep.snapshot) {
1744 do_pgk(TT.pgrep.snapshot);
1745 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1747 if (TT.pgrep.d) xputc('\n');
1750 #define CLEANUP_pgrep
1752 #include "generated/flags.h"
1754 void pkill_main(void)
1756 char **args = toys.optargs;
1758 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1759 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1760 if (toys.optflags & FLAG_V) TT.tty = 1;