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 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
33 * TODO: switch -fl to -y
34 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
35 * TODO: iotop: Window size change: respond immediately. Why not padding
36 * at right edge? (Not adjusting to screen size at all? Header wraps?)
38 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflno*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
39 // stayroot because iotop needs root to read other process' proc/$$/io
40 USE_TOP(NEWTOY(top, ">0m" "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
41 USE_IOTOP(NEWTOY(iotop, ">0Aako" "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
42 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:", TOYFLAG_USR|TOYFLAG_BIN))
43 USE_PKILL(NEWTOY(pkill, "Vu*U*t*s*P*g*G*fnovxl:", TOYFLAG_USR|TOYFLAG_BIN))
49 usage: ps [-AadeflnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
53 Which processes to show (selections may be comma separated lists):
56 -a Processes with terminals that aren't session leaders
57 -d All processes that aren't session leaders
59 -g Belonging to GROUPs
60 -G Belonging to real GROUPs (before sgid)
62 -P Parent PIDs (--ppid)
64 -t Attached to selected TTYs
66 -U Owned by real USERs (before suid)
70 -k Sort FIELDs in +increasing or -decreasting order (--sort)
71 -n Show numeric USER and GROUP
72 -w Wide output (don't truncate at terminal width)
74 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
76 -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
77 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
78 -o Output the listed FIELDs, each with optional :size and/or =title
83 ADDR Instruction pointer ARGS Command line (argv[] -path)
84 CMD COMM without -f, ARGS with -f CMDLINE Command line (argv[])
85 COMM Original command name COMMAND Original command path
86 CPU Which processor running on ETIME Elapsed time since PID start
87 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
88 GROUP Group name LABEL Security label
89 MAJFL Major page faults MINFL Minor page faults
90 NAME Command name (argv[0]) NI Niceness (lower is faster)
91 PCPU Percentage of CPU time used PGID Process Group ID
92 PID Process ID PPID Parent Process ID
93 PRI Priority (higher is faster) PSR Processor last executed on
94 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
95 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
96 RUID Real (before suid) user ID RUSER Real (before suid) user name
98 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
99 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
100 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
101 STAT Process state (S) plus:
102 < high priority N low priority L locked memory
103 s session leader + foreground l multithreaded
104 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
105 SZ Memory Size (4k pages needed to completely swap out process)
106 TIME CPU time consumed TTY Controlling terminal
107 UID User id USER User name
108 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
109 WCHAN Waiting in kernel for
115 usage: top [-m] [ -d seconds ] [ -n iterations ]
117 Provide a view of process activity in real time.
119 N/M/P/T show CPU usage, sort by pid/mem/cpu/time
127 -n Iterations before exiting
128 -d Delay between updates
131 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
138 Rank processes by I/O.
140 -A All I/O, not just disk
141 -a Accumulated I/O (not percentage)
143 -o Only show processes doing I/O
145 Cursor left/right to change sort, space to update, Q to exit.
151 usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
152 -b Batch mode (no tty)
153 -d Delay SECONDS between each cycle (default 3)
154 -n Exit after NUMBER iterations
157 -q Quiet (no header lines)
162 depends on PGKILL_COMMON
164 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
166 Search for process(es). PATTERN is an extended regular expression checked
167 against command names.
169 -c Show only count of matches
170 -d Use DELIM instead of newline
171 -L Send SIGNAL instead of printing name
178 usage: pgrep [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
180 -f Check full command line for PATTERN
181 -G Match real Group ID(s)
182 -g Match Process Group(s) (0 is current user)
185 -P Match Parent Process ID(s)
186 -s Match Session ID(s) (0 for current)
188 -U Match real User ID(s)
189 -u Match effective User ID(s)
191 -x Match whole command (not substring)
197 usage: pkill [-l SIGNAL] [PATTERN]
244 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
245 unsigned width, height;
247 void *fields, *kfields;
248 long long ticks, bits, ioread, iowrite, aioread, aiowrite;
250 int kcount, forcek, sortpos;
251 int (*match_process)(long long *slot);
252 void (*show_process)(void *tb);
256 struct strawberry *next, *prev;
257 short which, len, reverse;
262 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
263 * table 1-4) but we shift and repurpose fields, with the result being: */
266 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
267 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
268 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
269 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
270 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
271 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
272 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
273 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
274 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
275 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
276 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
277 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
278 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
279 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
280 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
281 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
282 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
283 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
284 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
285 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
286 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
287 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
288 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
289 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
290 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
291 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
292 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
293 SLOT_swap, /*Swap pages used*/
296 // Data layout in toybuf
298 long long slot[55]; // data from /proc
299 unsigned short offset[5]; // offset of fields in str[] (skip name, always 0)
301 char str[]; // name, tty, command, wchan, attr, cmdline
304 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
305 // 64|slot means compare as string when sorting
308 signed char width, slot;
309 } static const typos[] = TAGGED_ARRAY(PS,
311 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
312 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
313 {"SZ", 5, SLOT_vsize}, {"RSS", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
314 {"VSZ", 6, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
315 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
316 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
319 {"COMM", -15, -1}, {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4},
320 {"COMMAND", -27, -5}, {"CMDLINE", -27, -6}, {"ARGS", -27, -6},
321 {"NAME", -15, -6}, {"CMD", -27, -1},
324 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
325 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
326 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
329 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
330 {"TIME+", 9, SLOT_utime},
332 // Percentage displays
333 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
334 {"%CPU", 4, SLOT_utime2},
337 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
338 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
339 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
340 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
343 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
349 // Return 0 to discard, nonzero to keep
350 static int shared_match_process(long long *slot)
352 struct ptr_len match[] = {
353 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
354 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
355 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
360 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
361 for (i = 0; i < ARRAY_LEN(match); i++) {
362 struct ptr_len *mm = match[i].ptr;
365 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
373 // Return 0 to discard, nonzero to keep
374 static int ps_match_process(long long *slot)
376 int i = shared_match_process(slot);
379 // If we had selections and didn't match them, don't display
382 // Filter implicit categories for other display types
383 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
384 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
385 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
386 && TT.tty!=slot[SLOT_ttynr]) return 0;
391 // Convert field to string representation
392 static char *string_field(struct carveup *tb, struct strawberry *field)
394 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
395 int which = field->which, sl = typos[which].slot;
396 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
398 // numbers, mostly from /proc/$PID/stat
399 if (which <= PS_CPU) {
402 if (which==PS_PRI) ll = 39-ll;
403 if (which==PS_ADDR) fmt = "%llx";
404 else if (which==PS_SZ) ll >>= 12;
405 else if (which==PS_RSS) ll <<= 2;
406 else if (which==PS_VSZ) ll >>= 10;
407 else if (which==PS_PR && ll<-9) fmt="RT";
408 else if (which==PS_RTPRIO && ll == 0) fmt="-";
409 sprintf(out, fmt, ll);
413 if (slot[SLOT_argv0len])
414 tb->str[tb->offset[4]+slot[SLOT_argv0len]] = (which==PS_NAME) ? 0 : ' ';
417 if (--sl) out += tb->offset[--sl];
419 for (s = out; *s && *s != ' '; s++) if (*s == '/') out = s+1;
420 if (which>=PS_COMMAND && !*out) sprintf(out = buf, "[%s]", tb->str);
423 } else if (which <= PS_RGROUP) {
424 sprintf(out, "%lld", ll);
426 if (which > PS_RUSER) {
427 struct group *gr = getgrgid(ll);
429 if (gr) out = gr->gr_name;
431 struct passwd *pw = getpwuid(ll);
433 if (pw) out = pw->pw_name;
438 } else if (which <= PS_TIME_) {
439 int unit = 60, pad = 2, j = TT.ticks;
442 if (which!=PS_TIME_) unit *= 60*24;
444 // top adjusts slot[SLOT_upticks], we want original meaning.
445 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
448 // Output days-hours:mins:secs, skipping non-required fields with zero
449 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
450 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
451 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
453 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
455 if ((*s = "-::"[j])) s++;
460 if (which==PS_TIME_ && s-out<8)
461 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
463 // Percentage displays
464 } else if (which <= PS__CPU) {
465 ll = slot[sl&63]*1000;
466 if (which==PS__VSZ || which==PS__MEM)
467 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
468 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
470 if (which==PS_C) sl += 5;
471 sprintf(out, "%d", sl/10);
472 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
475 } else if (which <= PS_DIO) {
476 ll = slot[typos[which].slot];
477 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
478 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
479 else human_readable(out, ll, 0);
481 // Posix doesn't specify what flags should say. Man page says
482 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
483 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
484 else if (which==PS_S || which==PS_STAT) {
487 if (which==PS_STAT) {
488 // TODO l = multithreaded
489 if (slot[SLOT_nice]<0) *s++ = '<';
490 else if (slot[SLOT_nice]>0) *s++ = 'N';
491 if (slot[SLOT_sid]==*slot) *s++ = 's';
492 if (slot[SLOT_vmlck]) *s++ = 'L';
493 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
496 } else if (which==PS_STIME) {
497 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
499 // Padding behavior's a bit odd: default field size is just hh:mm.
500 // Increasing stime:size reveals more data at left until full,
501 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
502 // then add :ss on right for :19.
503 strftime(out, 260, "%F %T", localtime(&t));
504 out = out+strlen(out)-3-abs(field->len);
505 if (out<buf) out = buf;
507 } else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
512 // Display process data that get_ps() read from /proc, formatting with TT.fields
513 static void show_ps(struct carveup *tb)
515 struct strawberry *field;
516 int pad, len, width = TT.width;
518 // Loop through fields to display
519 for (field = TT.fields; field; field = field->next) {
520 char *out = string_field(tb, field);
522 // Output the field, appropriately padded
523 if (field != TT.fields) {
529 if (field->next || field->len>0)
530 len = abs(pad = width<abs(field->len) ? width : field->len);
532 if (TT.tty) width -= draw_trim(out, pad, len);
533 else width -= printf("%*.*s", pad, len, out);
539 // dirtree callback: read data about process to display, store, or discard it.
540 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
541 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
542 static int get_ps(struct dirtree *new)
548 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
549 {"exe", _PS_COMMAND}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}
551 struct carveup *tb = (void *)toybuf;
552 long long *slot = tb->slot;
553 char *name, *s, *buf = tb->str, *end = 0;
557 // Recurse one level into /proc children, skip non-numeric entries
559 return DIRTREE_RECURSE|DIRTREE_SHUTUP|(DIRTREE_SAVE*!TT.show_process);
561 memset(slot, 0, sizeof(tb->slot));
562 if (!(*slot = atol(new->name))) return 0;
563 fd = dirtree_parentfd(new);
566 sprintf(buf, "%lld/stat", *slot);
567 if (!readfileat(fd, buf, buf, &len)) return 0;
569 // parse oddball fields (name and state). Name can have embedded ')' so match
570 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
571 // All remaining fields should be numeric.
572 if (!(name = strchr(buf, '('))) return 0;
573 for (s = ++name; *s; s++) if (*s == ')') end = s;
574 if (!end || end-name>255) return 0;
576 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
577 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
578 for (j = 1; j<50; j++) if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
580 // Now we've read the data, move status and name right after slot[] array,
581 // and convert low chars to ? for non-tty display while we're at it.
582 for (i = 0; i<end-name; i++)
583 if ((tb->str[i] = name[i]) < ' ')
584 if (!TT.tty) tb->str[i] = '?';
587 len = sizeof(toybuf)-(buf-toybuf);
589 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
590 // or numeric wchan, and the remaining two are always zero), and vmlck into
591 // 18 (which is "obsolete, always 0" from stat)
592 slot[SLOT_uid] = new->st.st_uid;
593 slot[SLOT_gid] = new->st.st_gid;
595 // TIME and TIME+ use combined value, ksort needs 'em added.
596 slot[SLOT_utime] += slot[SLOT_stime];
597 slot[SLOT_utime2] = slot[SLOT_utime];
599 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
600 // and save ruid, rgid, and vmlck.
601 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
602 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
606 sprintf(buf, "%lld/status", *slot);
607 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
608 s = strafter(buf, "\nUid:");
609 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
610 s = strafter(buf, "\nGid:");
611 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
612 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
613 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
616 // Do we need to read "io"?
617 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
620 sprintf(buf, "%lld/io", *slot);
621 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
622 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
623 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
624 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
625 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
626 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
627 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
630 // We now know enough to skip processes we don't care about.
631 if (TT.match_process && !TT.match_process(slot)) return 0;
633 // /proc data is generated as it's read, so for maximum accuracy on slow
634 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
636 slot[SLOT_uptime] = TT.si.uptime;
637 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_rss];
639 // Do we need to read "statm"?
640 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
643 sprintf(buf, "%lld/statm", *slot);
644 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
646 for (s = buf, i=0; i<3; i++)
647 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
651 // Fetch string data while parentfd still available, appending to buf.
652 // (There's well over 3k of toybuf left. We could dynamically malloc, but
653 // it'd almost never get used, querying length of a proc file is awkward,
654 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
655 slot[SLOT_argv0len] = 0;
656 for (j = 0; j<ARRAY_LEN(fetch); j++) {
657 tb->offset[j] = buf-(tb->str);
658 if (!(TT.bits&fetch[j].bits)) {
663 // Determine remaining space, reserving minimum of 256 bytes/field and
664 // 260 bytes scratch space at the end (for output conversion later).
665 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
666 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
668 // For cmdline we readlink instead of read contents
670 if ((len = readlinkat(fd, buf, buf, len))>0) buf[len] = 0;
673 // If it's not the TTY field, data we want is in a file.
674 // Last length saved in slot[] is command line (which has embedded NULs)
676 int rdev = slot[SLOT_ttynr];
679 // Call no tty "?" rather than "0:0".
682 // Can we readlink() our way to a name?
683 for (i = 0; i<3; i++) {
684 sprintf(buf, "%lld/fd/%i", *slot, i);
685 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
686 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
693 // Couldn't find it, try all the tty drivers.
695 FILE *fp = fopen("/proc/tty/drivers", "r");
696 int tty_major = 0, maj = major(rdev), min = minor(rdev);
699 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
700 // TODO: we could parse the minor range too.
701 if (tty_major == maj) {
702 sprintf(buf+strlen(buf), "%d", min);
703 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
711 // Really couldn't find it, so just show major:minor.
712 if (!tty_major) sprintf(buf, "%d:%d", maj, min);
716 if (strstart(&s, "/dev/")) memmove(buf, s, strlen(s)+1);
719 // Data we want is in a file.
720 // Last length saved in slot[] is command line (which has embedded NULs)
723 // When command has no arguments, don't space over the NUL
724 if (readfileat(fd, buf, buf, &len) && len>0) {
727 if (buf[len-1]=='\n') buf[--len] = 0;
729 // Turn NUL to space, other low ascii to ? (in non-tty mode)
730 for (i=0; i<len; i++) {
736 } else if (!TT.tty && c<' ') c = '?';
739 len = temp; // position of _first_ NUL
740 } else *buf = len = 0;
741 // Store end of argv[0] so NAME and CMDLINE can differ.
742 slot[SLOT_argv0len] = len;
745 buf += strlen(buf)+1;
748 if (TT.show_process) {
754 // If we need to sort the output, add it to the list and return.
755 s = xmalloc(buf-toybuf);
756 new->extra = (long)s;
757 memcpy(s, toybuf, buf-toybuf);
763 static char *parse_ko(void *data, char *type, int length)
765 struct strawberry *field;
766 char *width, *title, *end, *s;
769 // Get title, length of title, type, end of type, and display width
771 // Chip off =name to display
772 if ((end = strchr(type, '=')) && length>(end-type)) {
774 length -= (end-type)+1;
780 // Chip off :width to display
781 if ((width = strchr(type, ':')) && width<end) {
782 if (!title) length = width-type;
785 // Allocate structure, copy title
786 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
788 memcpy(field->title = field->forever, title, length);
789 field->title[field->len = length] = 0;
793 field->len = strtol(++width, &title, 10);
794 if (!isdigit(*width) || title != end) return title;
799 if (*(struct strawberry **)data == TT.kfields) {
801 if (*type == '-') field->reverse = -1;
802 else if (*type != '+') type--;
805 for (i = 0; i<ARRAY_LEN(typos); i++) {
807 for (j = 0; j<2; j++) {
808 if (!j) s = typos[i].name;
809 // posix requires alternate names for some fields
810 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
811 PS_VSZ, PS_USER, 0}, i))) continue;
813 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
815 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
819 if (i==ARRAY_LEN(typos)) return type;
820 if (!field->title) field->title = typos[field->which].name;
821 if (!field->len) field->len = typos[field->which].width;
822 else if (typos[field->which].width<0) field->len *= -1;
823 dlist_add_nomalloc(data, (void *)field);
825 // Print padded header for -o.
826 if (*(struct strawberry **)data == TT.fields) {
828 snprintf(toybuf + TT.header_len, sizeof(toybuf) - TT.header_len,
829 " %*s" + (field == TT.fields), field->len, field->title);
830 TT.bits |= 1LL<<field->which;
836 // Parse -p -s -t -u -U -g -G
837 static char *parse_rest(void *data, char *str, int len)
839 struct ptr_len *pl = (struct ptr_len *)data;
844 // Allocate next chunk of data
846 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
848 // Parse numerical input
850 ll[pl->len] = xstrtol(str, &end, 10);
851 if (end==(len+str)) num++;
854 if (pl==&TT.pp || pl==&TT.ss) {
855 if (num && ll[pl->len]>0) {
860 } else if (pl==&TT.tt) {
861 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
863 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
864 if (strstart(&str, "pts/")) {
867 } else if (strstart(&str, "tty")) len -= 3;
869 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
872 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
873 memcpy(end, str, len);
876 ll[pl->len++] = st.st_rdev;
880 } else if (len<255) {
889 memcpy(name, str, len);
891 if (pl==&TT.gg || pl==&TT.GG) {
892 struct group *gr = getgrnam(name);
894 ll[pl->len++] = gr->gr_gid;
898 } else if (pl==&TT.uu || pl==&TT.UU) {
899 struct passwd *pw = getpwnam(name);
901 ll[pl->len++] = pw->pw_uid;
913 static int ksort(void *aa, void *bb)
915 struct strawberry *field;
916 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
919 for (field = TT.kfields; field && !ret; field = field->next) {
920 slot = typos[field->which].slot;
922 // Can we do numeric sort?
924 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
925 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
928 // fallback to string sort
930 memccpy(toybuf, string_field(ta, field), 0, 2048);
932 ret = strcmp(toybuf, string_field(tb, field));
934 ret *= field->reverse;
940 static struct carveup **collate(int count, struct dirtree *dt,
941 int (*sort)(void *a, void *b))
943 struct dirtree *temp;
944 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
947 // descend into child list
948 *tbsort = (void *)dt;
953 for (i = 0; i < count; i++) {
955 tbsort[i] = (void *)dt->extra;
963 static void shared_main(void)
967 TT.ticks = sysconf(_SC_CLK_TCK);
971 terminal_size(&TT.width, &TT.height);
974 // find controlling tty, falling back to /dev/tty if none
975 for (i = 0; !TT.tty && i<4; i++) {
979 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
981 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
991 if (toys.optflags&FLAG_w) TT.width = 99999;
994 // parse command line options other than -o
995 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
996 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
997 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
998 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
999 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1000 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1001 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1002 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1003 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1004 dlist_terminate(TT.kfields);
1006 // Parse manual field selection, or default/-f/-l, plus -Z,
1007 // constructing the header line in toybuf as we go.
1008 if (toys.optflags&FLAG_Z) {
1009 struct arg_list Z = { 0, "LABEL" };
1011 comma_args(&Z, &TT.fields, "-Z", parse_ko);
1013 if (TT.ps.o) comma_args(TT.ps.o, &TT.fields, "bad -o field", parse_ko);
1018 if (toys.optflags&FLAG_f)
1019 al.arg = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
1020 else if (toys.optflags&FLAG_l)
1021 al.arg = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1022 else if (CFG_TOYBOX_ON_ANDROID)
1023 al.arg = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,CMDLINE";
1024 else al.arg = "PID,TTY,TIME,CMD";
1026 comma_args(&al, &TT.fields, 0, parse_ko);
1028 dlist_terminate(TT.fields);
1029 printf("%s\n", toybuf);
1031 // misunderstand fields the flags say to
1032 if (toys.optflags&(FLAG_f|FLAG_n)) {
1033 struct strawberry *ever;
1035 for (ever = TT.fields; ever; ever = ever->next) {
1036 int alluc = ever->which;
1038 if ((toys.optflags&FLAG_f) && alluc==PS_CMD) alluc = PS_ARGS;
1039 if ((toys.optflags&FLAG_n) && alluc>=PS_UID && alluc<=PS_RGROUP
1040 && (typos[alluc].slot&64)) alluc--;
1041 if (alluc != ever->which) {
1042 TT.bits &= 1LL<<ever->which;
1043 TT.bits |= 1LL<<(ever->which = alluc);
1048 if (!(toys.optflags&FLAG_k)) TT.show_process = (void *)show_ps;
1049 TT.match_process = ps_match_process;
1050 dt = dirtree_read("/proc", get_ps);
1052 if (toys.optflags&FLAG_k) {
1053 struct carveup **tbsort = collate(TT.kcount, dt, ksort);
1055 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1056 for (i = 0; i<TT.kcount; i++) {
1060 if (CFG_TOYBOX_FREE) free(tbsort);
1063 if (CFG_TOYBOX_FREE) {
1072 llist_traverse(TT.fields, free);
1078 #include "generated/flags.h"
1080 // select which of the -o fields to sort by
1081 static void setsort(int pos)
1083 struct strawberry *field, *going2;
1088 for (field = TT.fields; field; field = field->next) {
1089 if ((TT.sortpos = i++)<pos) continue;
1090 going2 = TT.kfields;
1091 going2->which = field->which;
1092 going2->len = field->len;
1097 // If we have both, adjust slot[deltas[]] to be relative to previous
1098 // measurement rather than process start. Stomping old.data is fine
1099 // because we free it after displaying.
1100 static int merge_deltas(long long *oslot, long long *nslot)
1102 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_upticks,
1103 SLOT_rchar, SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1106 for (i = 0; i<ARRAY_LEN(deltas); i++)
1107 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1112 static void top_common(char *header,
1113 int (*filter)(long long *oslot, long long *nslot))
1116 long long timeout = 0, now;
1118 struct carveup **tb;
1120 } plist[2], *plold, *plnew, old, new, mix;
1123 int i, lines, done = 0;
1127 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1129 xset_terminal(0, 1, 0);
1130 sigatexit(tty_sigreset);
1134 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1135 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1137 TT.match_process = shared_match_process;
1138 memset(plist, 0, sizeof(plist));
1140 struct dirtree *dt = dirtree_read("/proc", get_ps);
1142 plold = plist+(tock++&1);
1143 plnew = plist+(tock&1);
1144 plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
1147 // First time, wait a quarter of a second to collect a little delta data.
1153 // Collate old and new into "mix", depends on /proc read in pid sort order
1156 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1159 while (old.count || new.count) {
1160 struct carveup *otb = *old.tb, *ntb = *new.tb;
1162 // If we just have old, discard it.
1163 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1170 // If we just have new, use it verbatim
1171 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1174 if (filter(otb->slot, ntb->slot)) {
1175 mix.tb[mix.count] = otb;
1185 // Will will re-fetch no data before its time. - Mork calling Orson Welles
1189 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1191 if (!(toys.optflags&FLAG_b)) printf("\033[H\033[J");
1192 if (!(toys.optflags&FLAG_q)) {
1194 strcpy(pos = toybuf, header);
1196 for (i=0, is = *pos; *pos; pos++) {
1199 if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
1200 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1203 printf("\033[7m%*.*s\033[0m\n\r",
1204 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf);
1206 if (!(toys.optflags&FLAG_b))
1207 terminal_probesize(&TT.width, &TT.height);
1209 lines = TT.height-2;
1211 for (i=0; i<lines && i<mix.count; i++) {
1216 if (TT.top.n && !--TT.top.n) {
1221 // Get current time in miliseconds
1222 clock_gettime(CLOCK_MONOTONIC, &ts);
1223 now = ts.tv_sec*1000+ts.tv_nsec/1000000;
1224 if (timeout<=now) timeout += TT.top.d;
1225 if (timeout<=now) timeout = now+TT.top.d;
1227 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1228 if (i==-1 || i==3 || toupper(i)=='Q') {
1234 // Flush unknown escape sequences.
1235 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1241 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1242 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1248 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1251 if (!(toys.optflags&FLAG_b)) tty_reset();
1261 al.arg = xstrdup("PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE");
1262 comma_args(&al, &TT.fields, 0, parse_ko);
1264 dlist_terminate(TT.fields);
1265 header=xstrdup(toybuf);
1268 al.arg = xstrdup("-S,-%CPU,-ETIME,-PID");
1269 comma_args(&al, &TT.kfields, "bang", parse_ko);
1270 dlist_terminate(TT.kfields);
1273 top_common(header, merge_deltas);
1278 #include "generated/flags.h"
1280 static int iotop_filter(long long *oslot, long long *nslot)
1282 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot);
1284 return !(toys.optflags&FLAG_o) || oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1287 void iotop_main(void)
1290 char *header, *d = "D"+!!(toys.optflags&FLAG_A);
1292 if (toys.optflags&FLAG_k) TT.forcek++;
1295 al.arg = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM", d, d, d);
1296 comma_args(&al, &TT.fields, 0, parse_ko);
1298 dlist_terminate(TT.fields);
1299 header = strdup(toybuf);
1301 // Fallback sorts. First (dummy) field gets overwritten by setsort()
1302 al.arg = xmprintf("-S,-%sIO,-ETIME,-PID",d);
1303 comma_args(&al, &TT.kfields, 0, parse_ko);
1305 dlist_terminate(TT.kfields);
1308 top_common(header, iotop_filter);
1311 // pkill's plumbing wrap's pgrep's and thus mostly takes place in pgrep's flag
1312 // context, so force pgrep's flags on even when building pkill standalone.
1313 // (All the pgrep/pkill functions drop out when building ps standalone.)
1315 #define CLEANUP_iotop
1317 #include "generated/flags.h"
1320 struct regex_list *next;
1324 static void show_pgrep(struct carveup *tb)
1327 struct regex_list *reg;
1328 char *name = tb->str;
1330 // Never match ourselves.
1331 if (TT.pgrep.self == *tb->slot) return;
1333 if (toys.optflags&FLAG_f) name += tb->offset[4];
1335 if (TT.pgrep.regexes) {
1336 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1337 if (regexec(®->reg, name, 1, &match, 0)) continue;
1338 if (toys.optflags&FLAG_x)
1339 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1342 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1345 // Repurpose a field for -c count
1347 if (TT.pgrep.signal) {
1348 if (kill(*tb->slot, TT.pgrep.signal)) {
1349 char *s = num_to_sig(TT.pgrep.signal);
1351 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1352 perror_msg("%s->%lld", s, *tb->slot);
1355 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1356 printf("%lld", *tb->slot);
1357 if (toys.optflags&FLAG_l) printf(" %s", name);
1359 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1363 static int pgrep_match_process(long long *slot)
1365 int match = shared_match_process(slot);
1367 return (toys.optflags&FLAG_v) ? !match : match;
1370 void pgrep_main(void)
1373 struct regex_list *reg;
1375 TT.pgrep.self = getpid();
1377 // No signal names start with "L", so no need for "L: " parsing.
1378 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1379 error_exit("bad -L '%s'", TT.pgrep.L);
1381 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1382 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1383 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1384 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1385 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1386 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1387 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1389 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1390 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1391 if (!toys.optc) help_exit("No PATTERN");
1393 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1394 for (arg = toys.optargs; *arg; arg++) {
1395 reg = xmalloc(sizeof(struct regex_list));
1396 xregcomp(®->reg, *arg, REG_EXTENDED);
1397 reg->next = TT.pgrep.regexes;
1398 TT.pgrep.regexes = reg;
1400 TT.match_process = pgrep_match_process;
1401 TT.show_process = (void *)show_pgrep;
1403 dirtree_read("/proc", get_ps);
1404 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1405 if (TT.pgrep.d) xputc('\n');
1408 #define CLEANUP_pgrep
1410 #include "generated/flags.h"
1412 void pkill_main(void)
1414 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1415 if (toys.optflags & FLAG_V) TT.tty = 1;