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]", 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 [-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
94 ADDR Instruction pointer ARGS Command line (argv[] -path)
95 BIT Is this process 32 or 64 bits
96 CMD COMM, or ARGS with -f CMDLINE Command line (argv[])
97 COMM Original command name COMMAND Original command path
98 CPU Which processor running on ETIME Elapsed time since PID start
99 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
100 GROUP Group name LABEL Security label
101 MAJFL Major page faults MINFL Minor page faults
102 NAME Command name (argv[0]) NI Niceness (lower is faster)
103 PCPU Percentage of CPU time used PCY Android scheduling policy
104 PGID Process Group ID
105 PID Process ID PPID Parent Process ID
106 PRI Priority (higher is faster) PSR Processor last executed on
107 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
108 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
109 RUID Real (before suid) user ID RUSER Real (before suid) user name
111 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
112 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
113 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
114 STAT Process state (S) plus:
115 < high priority N low priority L locked memory
116 s session leader + foreground l multithreaded
117 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
118 SZ Memory Size (4k pages needed to completely swap out process)
119 TCNT Thread count TID Thread ID
120 TIME CPU time consumed TTY Controlling terminal
121 UID User id USER User name
122 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
123 WCHAN Waiting in kernel for
129 usage: top [-m] [ -d seconds ] [ -n iterations ]
131 Show process activity in real time.
133 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
134 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
135 -s Sort by field number (1-X, default 9)
137 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
144 Rank processes by I/O.
146 -A All I/O, not just disk
147 -a Accumulated I/O (not percentage)
149 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
150 -O Only show processes doing I/O
151 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
152 -s Sort by field number (0-X, default 6)
158 usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
160 -b Batch mode (no tty)
161 -d Delay SECONDS between each cycle (default 3)
162 -n Exit after NUMBER iterations
165 -q Quiet (no header lines)
167 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
168 update, R to reverse sort, Q to exit.
173 depends on PGKILL_COMMON
175 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
177 Search for process(es). PATTERN is an extended regular expression checked
178 against command names.
180 -c Show only count of matches
181 -d Use DELIM instead of newline
182 -L Send SIGNAL instead of printing name
189 usage: pgrep [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
191 -f Check full command line for PATTERN
192 -G Match real Group ID(s)
193 -g Match Process Group(s) (0 is current user)
196 -P Match Parent Process ID(s)
197 -s Match Session ID(s) (0 for current)
199 -U Match real User ID(s)
200 -u Match effective User ID(s)
202 -x Match whole command (not substring)
208 usage: pkill [-l SIGNAL] [PATTERN]
252 void *regexes, *snapshot;
259 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
260 unsigned width, height;
262 void *fields, *kfields;
263 long long ticks, bits, time;
264 int kcount, forcek, sortpos;
265 int (*match_process)(long long *slot);
266 void (*show_process)(void *tb);
270 struct strawberry *next, *prev;
271 short which, len, reverse;
276 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
277 * table 1-4) but we shift and repurpose fields, with the result being: */
280 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
281 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
282 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
283 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
284 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
285 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
286 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
287 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
288 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
289 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
290 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
291 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
292 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
293 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
294 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
295 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
296 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
297 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
298 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
299 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
300 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
301 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
302 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
303 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
304 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
305 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
306 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
307 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
308 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
309 SLOT_pcy, /*Android sched policy*/
314 // Data layout in toybuf
316 long long slot[SLOT_count]; // data (see enum above)
317 unsigned short offset[5]; // offset of fields in str[] (skip name, always 0)
319 char str[]; // name, tty, command, wchan, attr, cmdline
322 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
323 // 64|slot means compare as string when sorting
326 signed char width, slot;
327 } static const typos[] = TAGGED_ARRAY(PS,
329 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
330 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
331 {"SZ", 5, SLOT_vsize}, {"RSS", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
332 {"VSZ", 6, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
333 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
334 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
335 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
338 {"COMM", -15, -1}, {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4},
339 {"COMMAND", -27, -5}, {"CMDLINE", -27, -6}, {"ARGS", -27, -6},
340 {"NAME", -15, -6}, {"CMD", -27, -1},
343 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
344 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
345 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
348 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
349 {"TIME+", 9, SLOT_utime},
351 // Percentage displays
352 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
353 {"%CPU", 4, SLOT_utime2},
356 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
357 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
358 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
359 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
362 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
363 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
366 // Return 0 to discard, nonzero to keep
367 static int shared_match_process(long long *slot)
369 struct ptr_len match[] = {
370 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
371 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
372 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
377 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
378 for (i = 0; i < ARRAY_LEN(match); i++) {
379 struct ptr_len *mm = match[i].ptr;
382 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
390 // Return 0 to discard, nonzero to keep
391 static int ps_match_process(long long *slot)
393 int i = shared_match_process(slot);
396 // If we had selections and didn't match them, don't display
399 // Filter implicit categories for other display types
400 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
401 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
402 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
403 && TT.tty!=slot[SLOT_ttynr]) return 0;
408 // Convert field to string representation
409 static char *string_field(struct carveup *tb, struct strawberry *field)
411 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
412 int which = field->which, sl = typos[which].slot;
413 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
415 // numbers, mostly from /proc/$PID/stat
416 if (which <= PS_BIT) {
419 if (which==PS_PRI) ll = 39-ll;
420 if (which==PS_ADDR) fmt = "%llx";
421 else if (which==PS_SZ) ll >>= 12;
422 else if (which==PS_RSS) ll <<= 2;
423 else if (which==PS_VSZ) ll >>= 10;
424 else if (which==PS_PR && ll<-9) fmt="RT";
425 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
426 sprintf(out, fmt, ll);
430 if (slot[SLOT_argv0len])
431 tb->str[tb->offset[4]+slot[SLOT_argv0len]] = (which==PS_NAME) ? 0 : ' ';
434 if (--sl) out += tb->offset[--sl];
436 for (s = out; *s && *s != ' '; s++) if (*s == '/') out = s+1;
437 if (which>=PS_COMMAND && (!*out || *slot != slot[SLOT_tid]))
438 sprintf(out = buf, "[%s]", tb->str);
441 } else if (which <= PS_RGROUP) {
442 sprintf(out, "%lld", ll);
444 if (which > PS_RUSER) {
445 struct group *gr = getgrgid(ll);
447 if (gr) out = gr->gr_name;
449 struct passwd *pw = getpwuid(ll);
451 if (pw) out = pw->pw_name;
456 } else if (which <= PS_TIME_) {
457 int unit = 60, pad = 2, j = TT.ticks;
460 if (which!=PS_TIME_) unit *= 60*24;
462 // top adjusts slot[SLOT_upticks], we want original meaning.
463 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
466 // Output days-hours:mins:secs, skipping non-required fields with zero
467 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
468 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
469 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
471 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
473 if ((*s = "-::"[j])) s++;
478 if (which==PS_TIME_ && s-out<8)
479 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
481 // Percentage displays
482 } else if (which <= PS__CPU) {
483 ll = slot[sl&63]*1000;
484 if (which==PS__VSZ || which==PS__MEM)
485 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
486 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
488 if (which==PS_C) sl += 5;
489 sprintf(out, "%d", sl/10);
490 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
493 } else if (which <= PS_DIO) {
494 ll = slot[typos[which].slot];
495 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
496 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
497 else human_readable(out, ll, 0);
499 // Posix doesn't specify what flags should say. Man page says
500 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
501 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
502 else if (which==PS_S || which==PS_STAT) {
505 if (which==PS_STAT) {
506 // TODO l = multithreaded
507 if (slot[SLOT_nice]<0) *s++ = '<';
508 else if (slot[SLOT_nice]>0) *s++ = 'N';
509 if (slot[SLOT_sid]==*slot) *s++ = 's';
510 if (slot[SLOT_vmlck]) *s++ = 'L';
511 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
514 } else if (which==PS_STIME) {
515 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
517 // Padding behavior's a bit odd: default field size is just hh:mm.
518 // Increasing stime:size reveals more data at left until full,
519 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
520 // then add :ss on right for :19.
521 strftime(out, 260, "%F %T", localtime(&t));
522 out = out+strlen(out)-3-abs(field->len);
523 if (out<buf) out = buf;
525 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
526 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
531 // Display process data that get_ps() read from /proc, formatting with TT.fields
532 static void show_ps(struct carveup *tb)
534 struct strawberry *field;
535 int pad, len, width = TT.width;
537 // Loop through fields to display
538 for (field = TT.fields; field; field = field->next) {
539 char *out = string_field(tb, field);
541 // Output the field, appropriately padded
542 if (field != TT.fields) {
548 if (field->next || field->len>0)
549 len = abs(pad = width<abs(field->len) ? width : field->len);
551 if (TT.tty) width -= draw_trim(out, pad, len);
552 else width -= printf("%*.*s", pad, len, out);
555 xputc(TT.time ? '\r' : '\n');
558 // dirtree callback: read data about process to display, store, or discard it.
559 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
560 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
561 static int get_ps(struct dirtree *new)
567 // sources for carveup->offset[] data
568 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
569 {"exe", _PS_COMMAND}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}
571 struct carveup *tb = (void *)toybuf;
572 long long *slot = tb->slot;
573 char *name, *s, *buf = tb->str, *end = 0;
577 // Recurse one level into /proc children, skip non-numeric entries
579 return DIRTREE_RECURSE|DIRTREE_SHUTUP|(DIRTREE_SAVE*!TT.show_process);
581 memset(slot, 0, sizeof(tb->slot));
582 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
583 fd = dirtree_parentfd(new);
586 sprintf(buf, "%lld/stat", *slot);
587 if (!readfileat(fd, buf, buf, &len)) return 0;
589 // parse oddball fields (name and state). Name can have embedded ')' so match
590 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
591 // All remaining fields should be numeric.
592 if (!(name = strchr(buf, '('))) return 0;
593 for (s = ++name; *s; s++) if (*s == ')') end = s;
594 if (!end || end-name>255) return 0;
596 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
597 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
598 for (j = 1; j<50; j++) if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
600 // Now we've read the data, move status and name right after slot[] array,
601 // and convert low chars to ? for non-tty display while we're at it.
602 for (i = 0; i<end-name; i++)
603 if ((tb->str[i] = name[i]) < ' ')
604 if (!TT.tty) tb->str[i] = '?';
607 len = sizeof(toybuf)-(buf-toybuf);
609 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
610 // or numeric wchan, and the remaining two are always zero), and vmlck into
611 // 18 (which is "obsolete, always 0" from stat)
612 slot[SLOT_uid] = new->st.st_uid;
613 slot[SLOT_gid] = new->st.st_gid;
615 // TIME and TIME+ use combined value, ksort needs 'em added.
616 slot[SLOT_utime] += slot[SLOT_stime];
617 slot[SLOT_utime2] = slot[SLOT_utime];
619 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
620 // and save ruid, rgid, and vmlck.
621 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
622 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
626 sprintf(buf, "%lld/status", *slot);
627 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
628 s = strafter(buf, "\nUid:");
629 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
630 s = strafter(buf, "\nGid:");
631 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
632 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
633 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
636 // Do we need to read "io"?
637 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
640 sprintf(buf, "%lld/io", *slot);
641 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
642 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
643 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
644 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
645 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
646 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
647 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
650 // We now know enough to skip processes we don't care about.
651 if (TT.match_process && !TT.match_process(slot)) return 0;
653 // /proc data is generated as it's read, so for maximum accuracy on slow
654 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
656 slot[SLOT_uptime] = TT.si.uptime;
657 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
659 // Do we need to read "statm"?
660 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
663 sprintf(buf, "%lld/statm", *slot);
664 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
666 for (s = buf, i=0; i<3; i++)
667 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
671 // Do we need to read "exe"?
672 if (TT.bits&_PS_BIT) {
675 sprintf(buf, "%lld/exe", *slot);
676 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
677 if (buf[4] == 1) slot[SLOT_bits] = 32;
678 else if (buf[4] == 2) slot[SLOT_bits] = 64;
682 // Do we need Android scheduling policy?
683 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
685 // Fetch string data while parentfd still available, appending to buf.
686 // (There's well over 3k of toybuf left. We could dynamically malloc, but
687 // it'd almost never get used, querying length of a proc file is awkward,
688 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
689 slot[SLOT_argv0len] = 0;
690 for (j = 0; j<ARRAY_LEN(fetch); j++) {
691 tb->offset[j] = buf-(tb->str);
692 if (!(TT.bits&fetch[j].bits)) {
697 // Determine remaining space, reserving minimum of 256 bytes/field and
698 // 260 bytes scratch space at the end (for output conversion later).
699 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
700 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
702 // For exe we readlink instead of read contents
704 if ((len = readlinkat(fd, buf, buf, len))>0) buf[len] = 0;
707 // If it's not the TTY field, data we want is in a file.
708 // Last length saved in slot[] is command line (which has embedded NULs)
710 int rdev = slot[SLOT_ttynr];
713 // Call no tty "?" rather than "0:0".
716 // Can we readlink() our way to a name?
717 for (i = 0; i<3; i++) {
718 sprintf(buf, "%lld/fd/%i", *slot, i);
719 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
720 && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
727 // Couldn't find it, try all the tty drivers.
729 FILE *fp = fopen("/proc/tty/drivers", "r");
730 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
733 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
734 // TODO: we could parse the minor range too.
735 if (tty_major == maj) {
736 sprintf(buf+strlen(buf), "%d", min);
737 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
745 // Really couldn't find it, so just show major:minor.
746 if (!tty_major) sprintf(buf, "%d:%d", maj, min);
750 if (strstart(&s, "/dev/")) memmove(buf, s, strlen(s)+1);
753 // Data we want is in a file.
754 // Last length saved in slot[] is command line (which has embedded NULs)
757 // When command has no arguments, don't space over the NUL
758 if (readfileat(fd, buf, buf, &len) && len>0) {
761 if (buf[len-1]=='\n') buf[--len] = 0;
763 // Turn NUL to space, other low ascii to ? (in non-tty mode)
764 // cmdline has a trailing NUL that we don't want to turn to space.
765 for (i=0; i<len-1; i++) {
771 } else if (!TT.tty && c<' ') c = '?';
774 len = temp; // position of _first_ NUL
775 } else *buf = len = 0;
776 // Store end of argv[0] so NAME and CMDLINE can differ.
777 slot[SLOT_argv0len] = len;
780 buf += strlen(buf)+1;
784 if (TT.show_process) {
790 // If we need to sort the output, add it to the list and return.
791 s = xmalloc(buf-toybuf);
792 new->extra = (long)s;
793 memcpy(s, toybuf, buf-toybuf);
798 static int get_threads(struct dirtree *new)
800 struct dirtree *threads, *dt;
801 unsigned pid, kcount;
802 void (*show_process)(void *tb) = TT.show_process;
804 if (!new->parent) return get_ps(new);
806 if (!(pid = atol(new->name))) return 0;
808 // Recurse down into tasks, retaining thread groups.
810 sprintf(toybuf, "/proc/%u/task", pid);
812 threads = dirtree_read(toybuf, get_ps);
813 if (!threads) return 0;
815 // Fill out tid and thread count for each entry in group
816 for (dt = threads->child; dt; dt = dt->next) {
817 struct carveup *tb = (void *)dt->extra;
819 tb->slot[SLOT_tid] = tb->slot[SLOT_pid];
820 tb->slot[SLOT_pid] = pid;
821 tb->slot[SLOT_tcount] = TT.kcount - kcount;
825 if (!(TT.show_process = show_process)) {
826 new->child = threads;
829 } while (threads->child) {
830 dt = threads->child->next;
831 show_process((void *)threads->child->extra);
832 free(threads->child);
839 static char *parse_ko(void *data, char *type, int length)
841 struct strawberry *field;
842 char *width, *title, *end, *s;
845 // Get title, length of title, type, end of type, and display width
847 // Chip off =name to display
848 if ((end = strchr(type, '=')) && length>(end-type)) {
850 length -= (end-type)+1;
856 // Chip off :width to display
857 if ((width = strchr(type, ':')) && width<end) {
858 if (!title) length = width-type;
861 // Allocate structure, copy title
862 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
864 memcpy(field->title = field->forever, title, length);
865 field->title[field->len = length] = 0;
869 field->len = strtol(++width, &title, 10);
870 if (!isdigit(*width) || title != end) return title;
876 if (*type == '-') field->reverse = -1;
877 else if (*type != '+') type--;
879 for (i = 0; i<ARRAY_LEN(typos); i++) {
881 for (j = 0; j<2; j++) {
882 if (!j) s = typos[i].name;
883 // posix requires alternate names for some fields
884 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
885 PS_VSZ, PS_USER, 0}, i))) continue;
887 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
889 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
893 if (i==ARRAY_LEN(typos)) return type;
894 if (!field->title) field->title = typos[field->which].name;
895 if (!field->len) field->len = typos[field->which].width;
896 else if (typos[field->which].width<0) field->len *= -1;
897 dlist_add_nomalloc(data, (void *)field);
902 long long get_headers(struct strawberry *fields, char *buf, int blen)
907 for (; fields; fields = fields->next) {
908 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
910 bits |= 1LL<<fields->which;
916 // Parse -p -s -t -u -U -g -G
917 static char *parse_rest(void *data, char *str, int len)
919 struct ptr_len *pl = (struct ptr_len *)data;
924 // Allocate next chunk of data
926 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
928 // Parse numerical input
930 ll[pl->len] = xstrtol(str, &end, 10);
931 if (end==(len+str)) num++;
934 if (pl==&TT.pp || pl==&TT.ss) {
935 if (num && ll[pl->len]>0) {
940 } else if (pl==&TT.tt) {
941 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
943 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
944 if (strstart(&str, "pts/")) {
947 } else if (strstart(&str, "tty")) len -= 3;
949 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
952 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
953 memcpy(end, str, len);
956 ll[pl->len++] = st.st_rdev;
960 } else if (len<255) {
969 memcpy(name, str, len);
971 if (pl==&TT.gg || pl==&TT.GG) {
972 struct group *gr = getgrnam(name);
974 ll[pl->len++] = gr->gr_gid;
978 } else if (pl==&TT.uu || pl==&TT.UU) {
979 struct passwd *pw = getpwnam(name);
981 ll[pl->len++] = pw->pw_uid;
993 static int ksort(void *aa, void *bb)
995 struct strawberry *field;
996 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
999 for (field = TT.kfields; field && !ret; field = field->next) {
1000 slot = typos[field->which].slot;
1002 // Can we do numeric sort?
1004 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1005 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1008 // fallback to string sort
1010 memccpy(toybuf, string_field(ta, field), 0, 2048);
1012 ret = strcmp(toybuf, string_field(tb, field));
1014 ret *= field->reverse;
1020 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1023 struct dirtree *next = dt->next;
1025 if (dt->child) tb = collate_leaves(tb, dt->child);
1026 else *(tb++) = (void *)dt->extra;
1034 static struct carveup **collate(int count, struct dirtree *dt)
1036 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1038 collate_leaves(tbsort, dt);
1043 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1045 struct arg_list def;
1047 memset(&def, 0, sizeof(struct arg_list));
1049 comma_args(arg ? arg : &def, fields, err, parse_ko);
1052 static void shared_main(void)
1056 TT.ticks = sysconf(_SC_CLK_TCK);
1058 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1060 terminal_size(&TT.width, &TT.height);
1063 // find controlling tty, falling back to /dev/tty if none
1064 for (i = 0; !TT.tty && i<4; i++) {
1068 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1070 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1071 if (i==3) close(fd);
1081 if (toys.optflags&FLAG_w) TT.width = 99999;
1084 // parse command line options other than -o
1085 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1086 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1087 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1088 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1089 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1090 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1091 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1092 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1093 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1094 dlist_terminate(TT.kfields);
1096 // Figure out which fields to display
1097 not_o = "%sTTY,TIME,CMD";
1098 if (toys.optflags&FLAG_f)
1099 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,CMD",
1100 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1101 else if (toys.optflags&FLAG_l)
1102 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1103 else if (CFG_TOYBOX_ON_ANDROID)
1104 not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1105 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1107 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1108 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1109 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1112 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1113 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1114 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1116 dlist_terminate(TT.fields);
1118 // -f and -n change the meaning of some fields
1119 if (toys.optflags&(FLAG_f|FLAG_n)) {
1120 struct strawberry *ever;
1122 for (ever = TT.fields; ever; ever = ever->next) {
1123 if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
1124 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1125 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1130 // Calculate seen fields bit array, and if we aren't deferring printing
1131 // print headers now (for low memory/nommu systems).
1132 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1133 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1134 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1135 TT.match_process = ps_match_process;
1136 dt = dirtree_read("/proc",
1137 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1138 ? get_threads : get_ps);
1140 if (toys.optflags&(FLAG_k|FLAG_M)) {
1141 struct carveup **tbsort = collate(TT.kcount, dt);
1143 if (toys.optflags&FLAG_M) {
1144 for (i = 0; i<TT.kcount; i++) {
1145 struct strawberry *field;
1147 for (field = TT.fields; field; field = field->next) {
1148 int len = strlen(string_field(tbsort[i], field));
1150 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1154 // Now that we've recalculated field widths, re-pad headers again
1155 get_headers(TT.fields, toybuf, sizeof(toybuf));
1156 printf("%.*s\n", TT.width, toybuf);
1159 if (toys.optflags&FLAG_k)
1160 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1161 for (i = 0; i<TT.kcount; i++) {
1165 if (CFG_TOYBOX_FREE) free(tbsort);
1168 if (CFG_TOYBOX_FREE) {
1177 llist_traverse(TT.fields, free);
1183 #include "generated/flags.h"
1185 // select which of the -o fields to sort by
1186 static void setsort(int pos)
1188 struct strawberry *field, *going2;
1193 for (field = TT.fields; field; field = field->next) {
1194 if ((TT.sortpos = i++)<pos && field->next) continue;
1195 going2 = TT.kfields;
1196 going2->which = field->which;
1197 going2->len = field->len;
1202 // If we have both, adjust slot[deltas[]] to be relative to previous
1203 // measurement rather than process start. Stomping old.data is fine
1204 // because we free it after displaying.
1205 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1207 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1208 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1211 for (i = 0; i<ARRAY_LEN(deltas); i++)
1212 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1213 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1218 static int header_line(int line, int rev)
1220 if (!line) return 0;
1222 if (toys.optflags&FLAG_b) rev = 0;
1224 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1225 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1226 rev ? "\033[0m" : "");
1231 static long long millitime(void)
1235 clock_gettime(CLOCK_MONOTONIC, &ts);
1236 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1239 static void top_common(
1240 int (*filter)(long long *oslot, long long *nslot, int milis))
1242 long long timeout = 0, now, stats[16];
1244 struct carveup **tb;
1247 } plist[2], *plold, *plnew, old, new, mix;
1248 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1249 "iow", "irq", "sirq", "host"};
1252 int i, lines, topoff = 0, done = 0;
1254 toys.signal = SIGWINCH;
1255 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1257 memset(plist, 0, sizeof(plist));
1258 memset(stats, 0, sizeof(stats));
1263 plold = plist+(tock++&1);
1264 plnew = plist+(tock&1);
1265 plnew->whence = millitime();
1266 dt = dirtree_read("/proc", get_ps);
1267 plnew->tb = collate(plnew->count = TT.kcount, dt);
1270 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1271 long long *st = stats+8*(tock&1);
1273 // user nice system idle iowait irq softirq host
1274 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1275 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1278 // First time, wait a quarter of a second to collect a little delta data.
1284 // Collate old and new into "mix", depends on /proc read in pid sort order
1287 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1290 while (old.count || new.count) {
1291 struct carveup *otb = *old.tb, *ntb = *new.tb;
1293 // If we just have old, discard it.
1294 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1301 // If we just have new, use it verbatim
1302 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1305 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1306 mix.tb[mix.count] = otb;
1316 // We will re-fetch no data before its time. - Mork calling Orson Welles
1321 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1322 if (!(toys.optflags&FLAG_b)) {
1323 printf("\033[H\033[J");
1326 terminal_probesize(&TT.width, &TT.height);
1331 if (recalc && !(toys.optflags&FLAG_q)) {
1332 if (*toys.which->name == 't') {
1333 struct strawberry alluc;
1334 long long ll, up = 0;
1339 memset(run, 0, sizeof(run));
1340 for (i = 0; i<mix.count; i++)
1341 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1344 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1345 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1346 lines = header_line(lines, 0);
1348 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1349 for (i=0; i<6; i++) {
1350 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1351 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1352 run[i] = pos ? atol(pos) : 0;
1355 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1356 run[0], run[0]-run[1], run[1], run[2]);
1357 lines = header_line(lines, 0);
1359 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1360 run[4], run[4]-run[5], run[5], run[3]);
1361 lines = header_line(lines, 0);
1365 i = sysconf(_SC_NPROCESSORS_CONF);
1366 pos += sprintf(pos, "%d%%cpu", i*100);
1369 // If a processor goes idle it's powered down and its idle ticks don't
1370 // advance, so calculate idle time as potential time - used.
1371 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1374 ll = stats[3] = stats[11] = 0;
1375 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1376 stats[3] = now - llabs(ll);
1378 for (i = 0; i<8; i++) {
1379 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1380 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1382 lines = header_line(lines, 0);
1384 struct strawberry *fields;
1387 memset(&tb, 0, sizeof(struct carveup));
1388 pos = stpcpy(toybuf, "Totals:");
1389 for (fields = TT.fields; fields; fields = fields->next) {
1390 long long ll, bits = 0;
1391 int slot = typos[fields->which].slot&63;
1393 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1394 ll = 1LL<<fields->which;
1395 if (bits&ll) continue;
1397 for (i=0; i<mix.count; i++)
1398 tb.slot[slot] += mix.tb[i]->slot[slot];
1399 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1400 " %s: %*s,", typos[fields->which].name,
1401 fields->len, string_field(&tb, fields));
1404 lines = header_line(lines, 0);
1407 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1408 for (i = 0, is = *pos; *pos; pos++) {
1411 if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
1412 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1415 lines = header_line(lines, 1);
1417 if (!recalc && !(toys.optflags&FLAG_b))
1418 printf("\033[%dH\033[J", 1+TT.height-lines);
1421 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1422 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1423 show_ps(mix.tb[i+topoff]);
1426 if (TT.top.n && !--TT.top.n) {
1432 if (timeout<=now) timeout = new.whence+TT.top.d;
1433 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1435 // In batch mode, we ignore the keyboard.
1436 if (toys.optflags&FLAG_b) {
1437 msleep(timeout-now);
1438 // Make an obvious gap between datasets.
1443 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1444 if (i==-1 || i==3 || toupper(i)=='Q') {
1450 // Flush unknown escape sequences.
1451 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1455 } else if (toupper(i)=='R')
1456 ((struct strawberry *)TT.kfields)->reverse *= -1;
1459 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1460 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1461 // KEY_UP is 0, so at end of strchr
1462 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1465 if (i == KEY_UP) topoff--;
1466 else if (i == KEY_DOWN) topoff++;
1467 else if (i == KEY_PGDN) topoff += lines;
1468 else if (i == KEY_PGUP) topoff -= lines;
1469 if (topoff<0) topoff = 0;
1470 if (topoff>mix.count) topoff = mix.count;
1477 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1481 if (!(toys.optflags&FLAG_b)) tty_reset();
1484 static void top_setup(char *defo, char *defk)
1489 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1491 TT.time = millitime();
1492 set_terminal(0, 1, 0);
1493 sigatexit(tty_sigreset);
1494 xsignal(SIGWINCH, generic_signal);
1495 printf("\033[?25l\033[0m");
1499 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1500 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1501 TT.match_process = shared_match_process;
1503 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1504 dlist_terminate(TT.fields);
1505 len = strlen(toybuf);
1506 if (toybuf[len-1]!=' ' && len<sizeof(toybuf)-1) strcpy(toybuf+len, " ");
1508 // First (dummy) sort field is overwritten by setsort()
1509 default_ko("-S", &TT.kfields, 0, 0);
1510 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1511 dlist_terminate(TT.kfields);
1512 setsort(TT.top.s-1);
1517 // usage: [-h HEADER] -o OUTPUT -k SORT
1520 "PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,ARGS",
1521 "-%CPU,-ETIME,-PID");
1522 top_common(merge_deltas);
1527 #include "generated/flags.h"
1529 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1531 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1532 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1534 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1537 void iotop_main(void)
1539 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1541 if (toys.optflags&FLAG_K) TT.forcek++;
1543 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1544 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1547 top_common(iotop_filter);
1550 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1551 // context, so force pgrep's flags on even when building pkill standalone.
1552 // (All the pgrep/pkill functions drop out when building ps standalone.)
1554 #define CLEANUP_iotop
1556 #include "generated/flags.h"
1559 struct regex_list *next;
1563 static void do_pgk(struct carveup *tb)
1565 if (TT.pgrep.signal) {
1566 if (kill(*tb->slot, TT.pgrep.signal)) {
1567 char *s = num_to_sig(TT.pgrep.signal);
1569 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1570 perror_msg("%s->%lld", s, *tb->slot);
1573 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1574 printf("%lld", *tb->slot);
1575 if (toys.optflags&FLAG_l)
1576 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1578 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1582 static void match_pgrep(struct carveup *tb)
1585 struct regex_list *reg;
1586 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1588 // Never match ourselves.
1589 if (TT.pgrep.self == *tb->slot) return;
1591 if (TT.pgrep.regexes) {
1592 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1593 if (regexec(®->reg, name, 1, &match, 0)) continue;
1594 if (toys.optflags&FLAG_x)
1595 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1598 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1601 // Repurpose a field for -c count
1603 if (toys.optflags&(FLAG_n|FLAG_o)) {
1604 long long ll = tb->slot[SLOT_starttime];
1606 if (toys.optflags&FLAG_o) ll *= -1;
1607 if (TT.time && TT.time>ll) return;
1609 free(TT.pgrep.snapshot);
1610 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1614 static int pgrep_match_process(long long *slot)
1616 int match = shared_match_process(slot);
1618 return (toys.optflags&FLAG_v) ? !match : match;
1621 void pgrep_main(void)
1624 struct regex_list *reg;
1626 TT.pgrep.self = getpid();
1628 // No signal names start with "L", so no need for "L: " parsing.
1629 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1630 error_exit("bad -L '%s'", TT.pgrep.L);
1632 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1633 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1634 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1635 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1636 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1637 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1638 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1640 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1641 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1642 if (!toys.optc) help_exit("No PATTERN");
1644 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1645 for (arg = toys.optargs; *arg; arg++) {
1646 reg = xmalloc(sizeof(struct regex_list));
1647 xregcomp(®->reg, *arg, REG_EXTENDED);
1648 reg->next = TT.pgrep.regexes;
1649 TT.pgrep.regexes = reg;
1651 TT.match_process = pgrep_match_process;
1652 TT.show_process = (void *)match_pgrep;
1654 dirtree_read("/proc", get_ps);
1655 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1656 if (TT.pgrep.snapshot) {
1657 do_pgk(TT.pgrep.snapshot);
1658 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1660 if (TT.pgrep.d) xputc('\n');
1663 #define CLEANUP_pgrep
1665 #include "generated/flags.h"
1667 void pkill_main(void)
1669 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1670 if (toys.optflags & FLAG_V) TT.tty = 1;