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*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
47 // stayroot because iotop needs root to read other process' proc/$$/io
48 USE_TOP(NEWTOY(top, ">0m" "k*o*p*u*s#<1=9d#=3<1n#<1bq", 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 [-AadeflnwZ] [-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
74 -U Owned by real USERs (before suid)
78 -k Sort FIELDs in +increasing or -decreasting order (--sort)
79 -M Measure field widths (expanding as necessary)
80 -n Show numeric USER and GROUP
81 -w Wide output (don't truncate at terminal width)
83 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
85 -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
86 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
87 -o Output FIELDs instead of defaults, each with optional :size and =title
88 -O Add FIELDS to defaults
93 ADDR Instruction pointer ARGS Command line (argv[] -path)
94 CMD COMM without -f, ARGS with -f CMDLINE Command line (argv[])
95 COMM Original command name COMMAND Original command path
96 CPU Which processor running on ETIME Elapsed time since PID start
97 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
98 GROUP Group name LABEL Security label
99 MAJFL Major page faults MINFL Minor page faults
100 NAME Command name (argv[0]) NI Niceness (lower is faster)
101 PCPU Percentage of CPU time used PGID Process Group ID
102 PID Process ID PPID Parent Process ID
103 PRI Priority (higher is faster) PSR Processor last executed on
104 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
105 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
106 RUID Real (before suid) user ID RUSER Real (before suid) user name
108 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
109 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
110 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
111 STAT Process state (S) plus:
112 < high priority N low priority L locked memory
113 s session leader + foreground l multithreaded
114 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
115 SZ Memory Size (4k pages needed to completely swap out process)
116 TIME CPU time consumed TTY Controlling terminal
117 UID User id USER User name
118 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
119 WCHAN Waiting in kernel for
125 usage: top [-m] [ -d seconds ] [ -n iterations ]
127 Show process activity in real time.
129 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
130 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
131 -s Sort by field number (1-X, default 9)
133 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
140 Rank processes by I/O.
142 -A All I/O, not just disk
143 -a Accumulated I/O (not percentage)
145 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
146 -O Only show processes doing I/O
147 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
148 -s Sort by field number (0-X, default 6)
154 usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
156 -b Batch mode (no tty)
157 -d Delay SECONDS between each cycle (default 3)
158 -n Exit after NUMBER iterations
161 -q Quiet (no header lines)
163 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
164 update, R to reverse sort, Q to exit.
169 depends on PGKILL_COMMON
171 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
173 Search for process(es). PATTERN is an extended regular expression checked
174 against command names.
176 -c Show only count of matches
177 -d Use DELIM instead of newline
178 -L Send SIGNAL instead of printing name
185 usage: pgrep [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
187 -f Check full command line for PATTERN
188 -G Match real Group ID(s)
189 -g Match Process Group(s) (0 is current user)
192 -P Match Parent Process ID(s)
193 -s Match Session ID(s) (0 for current)
195 -U Match real User ID(s)
196 -u Match effective User ID(s)
198 -x Match whole command (not substring)
204 usage: pkill [-l SIGNAL] [PATTERN]
248 void *regexes, *snapshot;
255 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
256 unsigned width, height;
258 void *fields, *kfields;
259 long long ticks, bits, time;
260 int kcount, forcek, sortpos;
261 int (*match_process)(long long *slot);
262 void (*show_process)(void *tb);
266 struct strawberry *next, *prev;
267 short which, len, reverse;
272 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
273 * table 1-4) but we shift and repurpose fields, with the result being: */
276 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
277 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
278 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
279 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
280 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
281 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
282 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
283 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
284 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
285 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
286 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
287 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
288 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
289 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
290 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
291 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
292 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
293 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
294 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
295 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
296 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
297 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
298 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
299 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
300 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
301 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
302 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
303 SLOT_swap, /*Swap pages used*/
306 // Data layout in toybuf
308 long long slot[55]; // data from /proc
309 unsigned short offset[5]; // offset of fields in str[] (skip name, always 0)
311 char str[]; // name, tty, command, wchan, attr, cmdline
314 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
315 // 64|slot means compare as string when sorting
318 signed char width, slot;
319 } static const typos[] = TAGGED_ARRAY(PS,
321 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
322 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
323 {"SZ", 5, SLOT_vsize}, {"RSS", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
324 {"VSZ", 6, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
325 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
326 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
329 {"COMM", -15, -1}, {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4},
330 {"COMMAND", -27, -5}, {"CMDLINE", -27, -6}, {"ARGS", -27, -6},
331 {"NAME", -15, -6}, {"CMD", -27, -1},
334 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
335 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
336 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
339 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
340 {"TIME+", 9, SLOT_utime},
342 // Percentage displays
343 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
344 {"%CPU", 4, SLOT_utime2},
347 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
348 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
349 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
350 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
353 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
357 // Return 0 to discard, nonzero to keep
358 static int shared_match_process(long long *slot)
360 struct ptr_len match[] = {
361 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
362 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
363 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
368 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
369 for (i = 0; i < ARRAY_LEN(match); i++) {
370 struct ptr_len *mm = match[i].ptr;
373 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
381 // Return 0 to discard, nonzero to keep
382 static int ps_match_process(long long *slot)
384 int i = shared_match_process(slot);
387 // If we had selections and didn't match them, don't display
390 // Filter implicit categories for other display types
391 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
392 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
393 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
394 && TT.tty!=slot[SLOT_ttynr]) return 0;
399 // Convert field to string representation
400 static char *string_field(struct carveup *tb, struct strawberry *field)
402 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
403 int which = field->which, sl = typos[which].slot;
404 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
406 // numbers, mostly from /proc/$PID/stat
407 if (which <= PS_CPU) {
410 if (which==PS_PRI) ll = 39-ll;
411 if (which==PS_ADDR) fmt = "%llx";
412 else if (which==PS_SZ) ll >>= 12;
413 else if (which==PS_RSS) ll <<= 2;
414 else if (which==PS_VSZ) ll >>= 10;
415 else if (which==PS_PR && ll<-9) fmt="RT";
416 else if (which==PS_RTPRIO && ll == 0) fmt="-";
417 sprintf(out, fmt, ll);
421 if (slot[SLOT_argv0len])
422 tb->str[tb->offset[4]+slot[SLOT_argv0len]] = (which==PS_NAME) ? 0 : ' ';
425 if (--sl) out += tb->offset[--sl];
427 for (s = out; *s && *s != ' '; s++) if (*s == '/') out = s+1;
428 if (which>=PS_COMMAND && !*out) sprintf(out = buf, "[%s]", tb->str);
431 } else if (which <= PS_RGROUP) {
432 sprintf(out, "%lld", ll);
434 if (which > PS_RUSER) {
435 struct group *gr = getgrgid(ll);
437 if (gr) out = gr->gr_name;
439 struct passwd *pw = getpwuid(ll);
441 if (pw) out = pw->pw_name;
446 } else if (which <= PS_TIME_) {
447 int unit = 60, pad = 2, j = TT.ticks;
450 if (which!=PS_TIME_) unit *= 60*24;
452 // top adjusts slot[SLOT_upticks], we want original meaning.
453 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
456 // Output days-hours:mins:secs, skipping non-required fields with zero
457 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
458 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
459 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
461 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
463 if ((*s = "-::"[j])) s++;
468 if (which==PS_TIME_ && s-out<8)
469 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
471 // Percentage displays
472 } else if (which <= PS__CPU) {
473 ll = slot[sl&63]*1000;
474 if (which==PS__VSZ || which==PS__MEM)
475 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
476 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
478 if (which==PS_C) sl += 5;
479 sprintf(out, "%d", sl/10);
480 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
483 } else if (which <= PS_DIO) {
484 ll = slot[typos[which].slot];
485 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
486 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
487 else human_readable(out, ll, 0);
489 // Posix doesn't specify what flags should say. Man page says
490 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
491 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
492 else if (which==PS_S || which==PS_STAT) {
495 if (which==PS_STAT) {
496 // TODO l = multithreaded
497 if (slot[SLOT_nice]<0) *s++ = '<';
498 else if (slot[SLOT_nice]>0) *s++ = 'N';
499 if (slot[SLOT_sid]==*slot) *s++ = 's';
500 if (slot[SLOT_vmlck]) *s++ = 'L';
501 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
504 } else if (which==PS_STIME) {
505 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
507 // Padding behavior's a bit odd: default field size is just hh:mm.
508 // Increasing stime:size reveals more data at left until full,
509 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
510 // then add :ss on right for :19.
511 strftime(out, 260, "%F %T", localtime(&t));
512 out = out+strlen(out)-3-abs(field->len);
513 if (out<buf) out = buf;
515 } else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
520 // Display process data that get_ps() read from /proc, formatting with TT.fields
521 static void show_ps(struct carveup *tb)
523 struct strawberry *field;
524 int pad, len, width = TT.width;
526 // Loop through fields to display
527 for (field = TT.fields; field; field = field->next) {
528 char *out = string_field(tb, field);
530 // Output the field, appropriately padded
531 if (field != TT.fields) {
537 if (field->next || field->len>0)
538 len = abs(pad = width<abs(field->len) ? width : field->len);
540 if (TT.tty) width -= draw_trim(out, pad, len);
541 else width -= printf("%*.*s", pad, len, out);
544 xputc(TT.time ? '\r' : '\n');
547 // dirtree callback: read data about process to display, store, or discard it.
548 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
549 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
550 static int get_ps(struct dirtree *new)
556 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
557 {"exe", _PS_COMMAND}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}
559 struct carveup *tb = (void *)toybuf;
560 long long *slot = tb->slot;
561 char *name, *s, *buf = tb->str, *end = 0;
565 // Recurse one level into /proc children, skip non-numeric entries
567 return DIRTREE_RECURSE|DIRTREE_SHUTUP|(DIRTREE_SAVE*!TT.show_process);
569 memset(slot, 0, sizeof(tb->slot));
570 if (!(*slot = atol(new->name))) return 0;
571 fd = dirtree_parentfd(new);
574 sprintf(buf, "%lld/stat", *slot);
575 if (!readfileat(fd, buf, buf, &len)) return 0;
577 // parse oddball fields (name and state). Name can have embedded ')' so match
578 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
579 // All remaining fields should be numeric.
580 if (!(name = strchr(buf, '('))) return 0;
581 for (s = ++name; *s; s++) if (*s == ')') end = s;
582 if (!end || end-name>255) return 0;
584 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
585 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
586 for (j = 1; j<50; j++) if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
588 // Now we've read the data, move status and name right after slot[] array,
589 // and convert low chars to ? for non-tty display while we're at it.
590 for (i = 0; i<end-name; i++)
591 if ((tb->str[i] = name[i]) < ' ')
592 if (!TT.tty) tb->str[i] = '?';
595 len = sizeof(toybuf)-(buf-toybuf);
597 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
598 // or numeric wchan, and the remaining two are always zero), and vmlck into
599 // 18 (which is "obsolete, always 0" from stat)
600 slot[SLOT_uid] = new->st.st_uid;
601 slot[SLOT_gid] = new->st.st_gid;
603 // TIME and TIME+ use combined value, ksort needs 'em added.
604 slot[SLOT_utime] += slot[SLOT_stime];
605 slot[SLOT_utime2] = slot[SLOT_utime];
607 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
608 // and save ruid, rgid, and vmlck.
609 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
610 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
614 sprintf(buf, "%lld/status", *slot);
615 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
616 s = strafter(buf, "\nUid:");
617 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
618 s = strafter(buf, "\nGid:");
619 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
620 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
621 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
624 // Do we need to read "io"?
625 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
628 sprintf(buf, "%lld/io", *slot);
629 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
630 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
631 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
632 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
633 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
634 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
635 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
638 // We now know enough to skip processes we don't care about.
639 if (TT.match_process && !TT.match_process(slot)) return 0;
641 // /proc data is generated as it's read, so for maximum accuracy on slow
642 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
644 slot[SLOT_uptime] = TT.si.uptime;
645 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
647 // Do we need to read "statm"?
648 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
651 sprintf(buf, "%lld/statm", *slot);
652 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
654 for (s = buf, i=0; i<3; i++)
655 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
659 // Fetch string data while parentfd still available, appending to buf.
660 // (There's well over 3k of toybuf left. We could dynamically malloc, but
661 // it'd almost never get used, querying length of a proc file is awkward,
662 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
663 slot[SLOT_argv0len] = 0;
664 for (j = 0; j<ARRAY_LEN(fetch); j++) {
665 tb->offset[j] = buf-(tb->str);
666 if (!(TT.bits&fetch[j].bits)) {
671 // Determine remaining space, reserving minimum of 256 bytes/field and
672 // 260 bytes scratch space at the end (for output conversion later).
673 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
674 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
676 // For cmdline we readlink instead of read contents
678 if ((len = readlinkat(fd, buf, buf, len))>0) buf[len] = 0;
681 // If it's not the TTY field, data we want is in a file.
682 // Last length saved in slot[] is command line (which has embedded NULs)
684 int rdev = slot[SLOT_ttynr];
687 // Call no tty "?" rather than "0:0".
690 // Can we readlink() our way to a name?
691 for (i = 0; i<3; i++) {
692 sprintf(buf, "%lld/fd/%i", *slot, i);
693 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
694 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
701 // Couldn't find it, try all the tty drivers.
703 FILE *fp = fopen("/proc/tty/drivers", "r");
704 int tty_major = 0, maj = major(rdev), min = minor(rdev);
707 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
708 // TODO: we could parse the minor range too.
709 if (tty_major == maj) {
710 sprintf(buf+strlen(buf), "%d", min);
711 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
719 // Really couldn't find it, so just show major:minor.
720 if (!tty_major) sprintf(buf, "%d:%d", maj, min);
724 if (strstart(&s, "/dev/")) memmove(buf, s, strlen(s)+1);
727 // Data we want is in a file.
728 // Last length saved in slot[] is command line (which has embedded NULs)
731 // When command has no arguments, don't space over the NUL
732 if (readfileat(fd, buf, buf, &len) && len>0) {
735 if (buf[len-1]=='\n') buf[--len] = 0;
737 // Turn NUL to space, other low ascii to ? (in non-tty mode)
738 for (i=0; i<len; i++) {
744 } else if (!TT.tty && c<' ') c = '?';
747 len = temp; // position of _first_ NUL
748 } else *buf = len = 0;
749 // Store end of argv[0] so NAME and CMDLINE can differ.
750 slot[SLOT_argv0len] = len;
753 buf += strlen(buf)+1;
757 if (TT.show_process) {
763 // If we need to sort the output, add it to the list and return.
764 s = xmalloc(buf-toybuf);
765 new->extra = (long)s;
766 memcpy(s, toybuf, buf-toybuf);
771 static char *parse_ko(void *data, char *type, int length)
773 struct strawberry *field;
774 char *width, *title, *end, *s;
777 // Get title, length of title, type, end of type, and display width
779 // Chip off =name to display
780 if ((end = strchr(type, '=')) && length>(end-type)) {
782 length -= (end-type)+1;
788 // Chip off :width to display
789 if ((width = strchr(type, ':')) && width<end) {
790 if (!title) length = width-type;
793 // Allocate structure, copy title
794 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
796 memcpy(field->title = field->forever, title, length);
797 field->title[field->len = length] = 0;
801 field->len = strtol(++width, &title, 10);
802 if (!isdigit(*width) || title != end) return title;
808 if (*type == '-') field->reverse = -1;
809 else if (*type != '+') type--;
811 for (i = 0; i<ARRAY_LEN(typos); i++) {
813 for (j = 0; j<2; j++) {
814 if (!j) s = typos[i].name;
815 // posix requires alternate names for some fields
816 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
817 PS_VSZ, PS_USER, 0}, i))) continue;
819 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
821 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
825 if (i==ARRAY_LEN(typos)) return type;
826 if (!field->title) field->title = typos[field->which].name;
827 if (!field->len) field->len = typos[field->which].width;
828 else if (typos[field->which].width<0) field->len *= -1;
829 dlist_add_nomalloc(data, (void *)field);
834 long long get_headers(struct strawberry *fields, char *buf, int blen)
839 for (; fields; fields = fields->next) {
840 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
842 bits |= 1LL<<fields->which;
848 // Parse -p -s -t -u -U -g -G
849 static char *parse_rest(void *data, char *str, int len)
851 struct ptr_len *pl = (struct ptr_len *)data;
856 // Allocate next chunk of data
858 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
860 // Parse numerical input
862 ll[pl->len] = xstrtol(str, &end, 10);
863 if (end==(len+str)) num++;
866 if (pl==&TT.pp || pl==&TT.ss) {
867 if (num && ll[pl->len]>0) {
872 } else if (pl==&TT.tt) {
873 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
875 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
876 if (strstart(&str, "pts/")) {
879 } else if (strstart(&str, "tty")) len -= 3;
881 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
884 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
885 memcpy(end, str, len);
888 ll[pl->len++] = st.st_rdev;
892 } else if (len<255) {
901 memcpy(name, str, len);
903 if (pl==&TT.gg || pl==&TT.GG) {
904 struct group *gr = getgrnam(name);
906 ll[pl->len++] = gr->gr_gid;
910 } else if (pl==&TT.uu || pl==&TT.UU) {
911 struct passwd *pw = getpwnam(name);
913 ll[pl->len++] = pw->pw_uid;
925 static int ksort(void *aa, void *bb)
927 struct strawberry *field;
928 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
931 for (field = TT.kfields; field && !ret; field = field->next) {
932 slot = typos[field->which].slot;
934 // Can we do numeric sort?
936 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
937 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
940 // fallback to string sort
942 memccpy(toybuf, string_field(ta, field), 0, 2048);
944 ret = strcmp(toybuf, string_field(tb, field));
946 ret *= field->reverse;
952 static struct carveup **collate(int count, struct dirtree *dt,
953 int (*sort)(void *a, void *b))
955 struct dirtree *temp;
956 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
959 // descend into child list
960 *tbsort = (void *)dt;
965 for (i = 0; i < count; i++) {
967 tbsort[i] = (void *)dt->extra;
975 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
979 memset(&def, 0, sizeof(struct arg_list));
981 comma_args(arg ? arg : &def, fields, err, parse_ko);
984 static void shared_main(void)
988 TT.ticks = sysconf(_SC_CLK_TCK);
990 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
992 terminal_size(&TT.width, &TT.height);
995 // find controlling tty, falling back to /dev/tty if none
996 for (i = 0; !TT.tty && i<4; i++) {
1000 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1002 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1003 if (i==3) close(fd);
1013 if (toys.optflags&FLAG_w) TT.width = 99999;
1016 // parse command line options other than -o
1017 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1018 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1019 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1020 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1021 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1022 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1023 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1024 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1025 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1026 dlist_terminate(TT.kfields);
1028 // Parse manual field selection, or default/-f/-l, plus -Z and -O
1029 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1030 if (toys.optflags&FLAG_f) s = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
1031 else if (toys.optflags&FLAG_l)
1032 s = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1033 else if (CFG_TOYBOX_ON_ANDROID)
1034 s = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1035 else s = "PID,TTY,TIME,CMD";
1036 default_ko(s, &TT.fields, "bad -o", TT.ps.o);
1038 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1039 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1040 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1042 dlist_terminate(TT.fields);
1044 // -f and -n change the meaning of some fields
1045 if (toys.optflags&(FLAG_f|FLAG_n)) {
1046 struct strawberry *ever;
1048 for (ever = TT.fields; ever; ever = ever->next) {
1049 if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1050 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1051 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1056 // Calculate seen fields bit array, and if we aren't deferring printing
1057 // print headers now (for low memory/nommu systems).
1058 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1059 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1060 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1061 TT.match_process = ps_match_process;
1062 dt = dirtree_read("/proc", get_ps);
1064 if (toys.optflags&(FLAG_k|FLAG_M)) {
1065 struct carveup **tbsort = collate(TT.kcount, dt, ksort);
1067 if (toys.optflags&FLAG_M) {
1068 for (i = 0; i<TT.kcount; i++) {
1069 struct strawberry *field;
1071 for (field = TT.fields; field; field = field->next) {
1072 int len = strlen(string_field(tbsort[i], field));
1074 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1078 // Now that we've recalculated field widths, re-pad headers again
1079 get_headers(TT.fields, toybuf, sizeof(toybuf));
1080 printf("%.*s\n", TT.width, toybuf);
1083 if (toys.optflags&FLAG_k)
1084 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1085 for (i = 0; i<TT.kcount; i++) {
1089 if (CFG_TOYBOX_FREE) free(tbsort);
1092 if (CFG_TOYBOX_FREE) {
1101 llist_traverse(TT.fields, free);
1107 #include "generated/flags.h"
1109 // select which of the -o fields to sort by
1110 static void setsort(int pos)
1112 struct strawberry *field, *going2;
1117 for (field = TT.fields; field; field = field->next) {
1118 if ((TT.sortpos = i++)<pos && field->next) continue;
1119 going2 = TT.kfields;
1120 going2->which = field->which;
1121 going2->len = field->len;
1126 // If we have both, adjust slot[deltas[]] to be relative to previous
1127 // measurement rather than process start. Stomping old.data is fine
1128 // because we free it after displaying.
1129 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1131 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1132 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1135 for (i = 0; i<ARRAY_LEN(deltas); i++)
1136 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1137 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1142 static int header_line(int line, int rev)
1144 if (!line) return 0;
1146 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1147 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1148 rev ? "\033[0m" : "");
1153 // Get current time in miliseconds
1154 static long long militime(void)
1158 clock_gettime(CLOCK_MONOTONIC, &ts);
1160 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1163 static void top_common(
1164 int (*filter)(long long *oslot, long long *nslot, int milis))
1166 long long timeout = 0, now, stats[16];
1168 struct carveup **tb;
1171 } plist[2], *plold, *plnew, old, new, mix;
1172 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1173 "iow", "irq", "sirq", "host"};
1176 int i, lines, topoff = 0, done = 0;
1178 toys.signal = SIGWINCH;
1179 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1181 memset(plist, 0, sizeof(plist));
1182 memset(stats, 0, sizeof(stats));
1187 plold = plist+(tock++&1);
1188 plnew = plist+(tock&1);
1189 plnew->whence = militime();
1190 dt= dirtree_read("/proc", get_ps);
1191 plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
1194 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1195 long long *st = stats+8*(tock&1);
1197 // user nice system idle iowait irq softirq host
1198 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1199 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1202 // First time, wait a quarter of a second to collect a little delta data.
1208 // Collate old and new into "mix", depends on /proc read in pid sort order
1211 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1214 while (old.count || new.count) {
1215 struct carveup *otb = *old.tb, *ntb = *new.tb;
1217 // If we just have old, discard it.
1218 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1225 // If we just have new, use it verbatim
1226 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1229 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1230 mix.tb[mix.count] = otb;
1240 // We will re-fetch no data before its time. - Mork calling Orson Welles
1245 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1246 if (!(toys.optflags&FLAG_b)) {
1247 printf("\033[H\033[J");
1250 terminal_probesize(&TT.width, &TT.height);
1255 if (recalc && !(toys.optflags&FLAG_q)) {
1256 if (*toys.which->name == 't') {
1257 struct strawberry alluc;
1258 long long ll, up = 0;
1263 memset(run, 0, sizeof(run));
1264 for (i = 0; i<mix.count; i++)
1265 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1268 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1269 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1270 lines = header_line(lines, 0);
1272 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1273 for (i=0; i<6; i++) {
1274 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1275 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1276 run[i] = pos ? atol(pos) : 0;
1279 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1280 run[0], run[0]-run[1], run[1], run[2]);
1281 lines = header_line(lines, 0);
1283 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1284 run[4], run[4]-run[5], run[5], run[3]);
1285 lines = header_line(lines, 0);
1289 i = sysconf(_SC_NPROCESSORS_CONF);
1290 pos += sprintf(pos, "%d%%cpu", i*100);
1293 // If a processor goes idle it's powered down and its idle ticks don't
1294 // advance, so calculate idle time as potential time - used.
1295 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1298 ll = stats[3] = stats[11] = 0;
1299 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1300 stats[3] = now - llabs(ll);
1302 for (i = 0; i<8; i++) {
1303 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1304 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1306 lines = header_line(lines, 0);
1308 struct strawberry *fields;
1311 memset(&tb, 0, sizeof(struct carveup));
1312 pos = stpcpy(toybuf, "Totals:");
1313 for (fields = TT.fields; fields; fields = fields->next) {
1314 long long ll, bits = 0;
1315 int slot = typos[fields->which].slot&63;
1317 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1318 ll = 1LL<<fields->which;
1319 if (bits&ll) continue;
1321 for (i=0; i<mix.count; i++)
1322 tb.slot[slot] += mix.tb[i]->slot[slot];
1323 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1324 " %s: %*s,", typos[fields->which].name,
1325 fields->len, string_field(&tb, fields));
1328 lines = header_line(lines, 0);
1331 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1332 for (i = 0, is = *pos; *pos; pos++) {
1335 if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
1336 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1339 lines = header_line(lines, 1);
1341 if (!recalc) printf("\033[%dH\033[J", 1+TT.height-lines);
1344 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1346 show_ps(mix.tb[i+topoff]);
1349 if (TT.top.n && !--TT.top.n) {
1354 // Get current time in miliseconds
1356 if (timeout<=now) timeout = new.whence+TT.top.d;
1357 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1359 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1360 if (i==-1 || i==3 || toupper(i)=='Q') {
1366 // Flush unknown escape sequences.
1367 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1371 } else if (toupper(i)=='R')
1372 ((struct strawberry *)TT.kfields)->reverse *= -1;
1375 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1376 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1377 // KEY_UP is 0, so at end of strchr
1378 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1381 if (i == KEY_UP) topoff--;
1382 else if (i == KEY_DOWN) topoff++;
1383 else if (i == KEY_PGDN) topoff += lines;
1384 else if (i == KEY_PGUP) topoff -= lines;
1385 if (topoff<0) topoff = 0;
1386 if (topoff>mix.count) topoff = mix.count;
1393 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1397 if (!(toys.optflags&FLAG_b)) tty_reset();
1400 static void top_setup(char *defo, char *defk)
1404 TT.time = militime();
1406 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1408 xset_terminal(0, 1, 0);
1409 sigatexit(tty_sigreset);
1410 xsignal(SIGWINCH, generic_signal);
1411 printf("\033[?25l\033[0m");
1415 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1416 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1417 TT.match_process = shared_match_process;
1419 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1420 dlist_terminate(TT.fields);
1421 len = strlen(toybuf);
1422 if (toybuf[len-1]!=' ' && len<sizeof(toybuf)-1) strcpy(toybuf+len, " ");
1424 // First (dummy) sort field is overwritten by setsort()
1425 default_ko("-S", &TT.kfields, 0, 0);
1426 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1427 dlist_terminate(TT.kfields);
1428 setsort(TT.top.s-1);
1433 // usage: [-h HEADER] -o OUTPUT -k SORT
1436 "PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,ARGS",
1437 "-%CPU,-ETIME,-PID");
1438 top_common(merge_deltas);
1443 #include "generated/flags.h"
1445 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1447 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1448 else oslot[SLOT_upticks] = ((militime()-TT.time)*TT.ticks)/1000;
1450 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1453 void iotop_main(void)
1455 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1457 if (toys.optflags&FLAG_K) TT.forcek++;
1459 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1460 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1463 top_common(iotop_filter);
1466 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1467 // context, so force pgrep's flags on even when building pkill standalone.
1468 // (All the pgrep/pkill functions drop out when building ps standalone.)
1470 #define CLEANUP_iotop
1472 #include "generated/flags.h"
1475 struct regex_list *next;
1479 static void do_pgk(struct carveup *tb)
1481 if (TT.pgrep.signal) {
1482 if (kill(*tb->slot, TT.pgrep.signal)) {
1483 char *s = num_to_sig(TT.pgrep.signal);
1485 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1486 perror_msg("%s->%lld", s, *tb->slot);
1489 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1490 printf("%lld", *tb->slot);
1491 if (toys.optflags&FLAG_l)
1492 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1494 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1498 static void match_pgrep(struct carveup *tb)
1501 struct regex_list *reg;
1502 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1504 // Never match ourselves.
1505 if (TT.pgrep.self == *tb->slot) return;
1507 if (TT.pgrep.regexes) {
1508 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1509 if (regexec(®->reg, name, 1, &match, 0)) continue;
1510 if (toys.optflags&FLAG_x)
1511 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1514 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1517 // Repurpose a field for -c count
1519 if (toys.optflags&(FLAG_n|FLAG_o)) {
1520 long long ll = tb->slot[SLOT_starttime];
1522 if (toys.optflags&FLAG_o) ll *= -1;
1523 if (TT.time && TT.time>ll) return;
1525 free(TT.pgrep.snapshot);
1526 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1530 static int pgrep_match_process(long long *slot)
1532 int match = shared_match_process(slot);
1534 return (toys.optflags&FLAG_v) ? !match : match;
1537 void pgrep_main(void)
1540 struct regex_list *reg;
1542 TT.pgrep.self = getpid();
1544 // No signal names start with "L", so no need for "L: " parsing.
1545 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1546 error_exit("bad -L '%s'", TT.pgrep.L);
1548 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1549 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1550 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1551 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1552 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1553 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1554 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1556 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1557 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1558 if (!toys.optc) help_exit("No PATTERN");
1560 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1561 for (arg = toys.optargs; *arg; arg++) {
1562 reg = xmalloc(sizeof(struct regex_list));
1563 xregcomp(®->reg, *arg, REG_EXTENDED);
1564 reg->next = TT.pgrep.regexes;
1565 TT.pgrep.regexes = reg;
1567 TT.match_process = pgrep_match_process;
1568 TT.show_process = (void *)match_pgrep;
1570 dirtree_read("/proc", get_ps);
1571 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1572 if (TT.pgrep.snapshot) {
1573 do_pgk(TT.pgrep.snapshot);
1574 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1576 if (TT.pgrep.d) xputc('\n');
1579 #define CLEANUP_pgrep
1581 #include "generated/flags.h"
1583 void pkill_main(void)
1585 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1586 if (toys.optflags & FLAG_V) TT.tty = 1;