1 /* ps.c - show process list
3 * Copyright 2015 Rob Landley <rob@landley.net>
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
6 * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
7 * And linux kernel source fs/proc/array.c function do_task_stat()
9 * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to
10 * mean "show numeric users and groups" instead.
11 * Posix says default output should have field named "TTY" but if you "-o tty"
12 * the same field should be called "TT" which is _INSANE_ and I'm not doing it.
13 * Similarly -f outputs USER but calls it UID (we call it USER).
14 * It also says that -o "args" and "comm" should behave differently but use
15 * the same title, which is not the same title as the default output. (No.)
16 * Select by session id is -s not -g. Posix doesn't say truncated fields
17 * should end with "+" but it's pretty common behavior.
19 * Posix defines -o ADDR as "The address of the process" but the process
20 * start address is a constant on any elf system with mmu. The procps ADDR
21 * field always prints "-" with an alignment of 1, which is why it has 11
22 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
23 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
24 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
25 * which changes -l by removing the "F" column and swapping RSS for ADDR,
26 * leaving 9 chars for cmd, so we're using that as our -l output.
28 * Added a bunch of new -o fields posix doesn't mention, and we don't
29 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
30 * output argv[0] unmodified for -o comm or -o args (but procps violates
31 * posix for -o comm anyway, it's stat[2] not argv[0]).
33 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
34 * files (why they're not globally readable when the rest of proc
35 * data is...?) and get a global I/O picture. Normal top is NOT,
36 * even though you can -o AIO there, to give sysadmins the option
37 * to reduce security exposure.)
39 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
40 * TODO: switch -fl to -y
41 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
42 * TODO: iotop: Window size change: respond immediately. Why not padding
43 * at right edge? (Not adjusting to screen size at all? Header wraps?)
44 * TODO: top: thread support and SMP
45 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
47 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
48 // stayroot because iotop needs root to read other process' proc/$$/io
49 // TOP and IOTOP have a large common option block used for common processing,
50 // the default values are different but the flags are in the same order.
51 USE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
52 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d#=3<1m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
53 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
54 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
60 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
64 Which processes to show (selections may be comma separated lists):
67 -a Processes with terminals that aren't session leaders
68 -d All processes that aren't session leaders
70 -g Belonging to GROUPs
71 -G Belonging to real GROUPs (before sgid)
73 -P Parent PIDs (--ppid)
75 -t Attached to selected TTYs
78 -U Owned by real USERs (before suid)
82 -k Sort FIELDs in +increasing or -decreasting order (--sort)
83 -M Measure field widths (expanding as necessary)
84 -n Show numeric USER and GROUP
85 -w Wide output (don't truncate fields)
87 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
89 -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
90 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
91 -o Output FIELDs instead of defaults, each with optional :size and =title
92 -O Add FIELDS to defaults
95 Command line -o fields:
97 ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
98 CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
99 COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
101 Process attribute -o FIELDs:
103 ADDR Instruction pointer BIT Is this process 32 or 64 bits
104 CPU Which processor running on ETIME Elapsed time since PID start
105 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
106 GROUP Group name LABEL Security label
107 MAJFL Major page faults MINFL Minor page faults
108 NI Niceness (lower is faster)
109 PCPU Percentage of CPU time used PCY Android scheduling policy
110 PGID Process Group ID
111 PID Process ID PPID Parent Process ID
112 PRI Priority (higher is faster) PSR Processor last executed on
113 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
114 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
115 RUID Real (before suid) user ID RUSER Real (before suid) user name
117 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
118 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
119 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
120 STAT Process state (S) plus:
121 < high priority N low priority L locked memory
122 s session leader + foreground l multithreaded
123 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
124 SZ Memory Size (4k pages needed to completely swap out process)
125 TCNT Thread count TID Thread ID
126 TIME CPU time consumed TTY Controlling terminal
127 UID User id USER User name
128 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
129 WCHAN What are we waiting in kernel for
135 usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]
137 Show process activity in real time.
140 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
141 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
142 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
143 -s Sort by field number (1-X, default 9)
144 -b Batch mode (no tty)
145 -d Delay SECONDS between each cycle (default 3)
146 -m Maximum number of tasks to show
147 -n Exit after NUMBER iterations
150 -q Quiet (no header lines)
152 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
153 update, R to reverse sort, Q to exit.
155 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
160 usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
162 Rank processes by I/O.
164 -A All I/O, not just disk
165 -a Accumulated I/O (not percentage)
168 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
169 -m Maximum number of tasks to show
170 -O Only show processes doing I/O
171 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
172 -s Sort by field number (0-X, default 6)
173 -b Batch mode (no tty)
174 -d Delay SECONDS between each cycle (default 3)
175 -n Exit after NUMBER iterations
178 -q Quiet (no header lines)
180 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
181 update, R to reverse sort, Q to exit.
187 usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
189 Search for process(es). PATTERN is an extended regular expression checked
190 against command names.
192 -c Show only count of matches
193 -d Use DELIM instead of newline
194 -L Send SIGNAL instead of printing name
196 -f Check full command line for PATTERN
197 -G Match real Group ID(s)
198 -g Match Process Group(s) (0 is current user)
201 -P Match Parent Process ID(s)
202 -s Match Session ID(s) (0 for current)
204 -U Match real User ID(s)
205 -u Match effective User ID(s)
207 -x Match whole command (not substring)
213 usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
215 -l Send SIGNAL (default SIGTERM)
217 -f Check full command line for PATTERN
218 -G Match real Group ID(s)
219 -g Match Process Group(s) (0 is current user)
222 -P Match Parent Process ID(s)
223 -s Match Session ID(s) (0 for current)
225 -U Match real User ID(s)
226 -u Match effective User ID(s)
228 -x Match whole command (not substring)
271 void *regexes, *snapshot;
278 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
279 struct dirtree *threadparent;
280 unsigned width, height;
282 void *fields, *kfields;
283 long long ticks, bits, time;
284 int kcount, forcek, sortpos;
285 int (*match_process)(long long *slot);
286 void (*show_process)(void *tb);
289 /* Linked list of fields selected for display, in order, with :len and =title */
292 struct strawberry *next, *prev;
293 short which, len, reverse;
298 /* The function get_ps() reads all the data about one process, saving it in
299 * toybox as a struct carveup. Simple ps calls then pass toybuf directly to
300 * show_ps(), but features like sorting instead append a copy to a linked list
301 * for further processing once all processes have been read.
303 * struct carveup contains a slot[] array of 64 bit values, with the following
304 * data at each position in the array. Most is read from /proc/$PID/stat (see
305 * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but
306 * we we replace several fields with don't use with other data. */
309 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
310 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
311 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
312 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
313 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
314 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
315 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime
316 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
317 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
318 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
319 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
320 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
321 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
322 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
323 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
324 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
325 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
326 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
327 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
328 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
329 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
330 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
331 SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length
332 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
333 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
334 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
335 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
336 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
337 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
338 SLOT_pcy, /*Android sched policy*/
340 SLOT_count /* Size of array */
343 /* In addition to slot[], carevup contains 6 string fields to display
344 command name, tty device, selinux label... They're stored one after the
345 other in str[] (separated by null terminators), and offset[] contains the
346 starting position of each string after the first (which is always 0). */
348 // Data layout in toybuf
350 long long slot[SLOT_count]; // data (see enum above)
351 unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0)
353 char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME
356 /* The typos[] array lists all the types understood by "ps -o", I.E all the
357 * columns ps and top know how to display. Each entry has:
359 * name: the column name, displayed at top and used to select column with -o
361 * width: the display width. Fields are padded to this width when displaying
362 * to a terminal (negative means right justified). Strings are truncated
363 * to fit, numerical fields are padded but not truncated (although
364 * the display code reclaims unused padding from later fields to try to
365 * get the overflow back).
367 * slot: which slot[] out of carveup. Negative means it's a string field.
368 * Setting bit |64 requests extra display/sort processing.
370 * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the
371 * first string argument and the prefix is the first argument to TAGGED_ARRAY
372 * so in this case "NAME" becomes PS_NAME which is the offset into typos[]
373 * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME).
374 * We record active columns in TT.bits, ala:
376 * if (TT.bits & _PS_NAME) printf("-o included PS_NAME");
379 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
380 // 64|slot means compare as string when sorting
383 signed char width, slot;
384 } static const typos[] = TAGGED_ARRAY(PS,
385 // Numbers. (What's in slot[] is what's displayed, sorted numerically.)
386 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
387 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
388 {"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
389 {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
390 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
391 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
392 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
394 // String fields (-1 is carveup->str, rest are str+offset[1-slot])
395 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
396 {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
397 {"ARGS", -27, -6}, {"CMD", -15, -1},
399 // user/group (may call getpwuid() or similar)
400 {"UID", 5, SLOT_uid}, {"USER", -12, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
401 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
402 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
404 // clock displays (00:00:00)
405 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
406 {"TIME+", 9, SLOT_utime},
408 // Percentage displays (fixed point, one decimal digit. 123 -> 12.3)
409 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
410 {"%CPU", 4, SLOT_utime2},
412 // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc)
413 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
414 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
415 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
416 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
418 // Misc (special cases)
419 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
420 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
423 // Return 0 to discard, nonzero to keep
424 static int shared_match_process(long long *slot)
426 struct ptr_len match[] = {
427 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
428 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
429 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
434 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
435 for (i = 0; i < ARRAY_LEN(match); i++) {
436 struct ptr_len *mm = match[i].ptr;
440 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
448 // Return 0 to discard, nonzero to keep
449 static int ps_match_process(long long *slot)
451 int i = shared_match_process(slot);
454 // If we had selections and didn't match them, don't display
457 // Filter implicit categories for other display types
458 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
459 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
460 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
461 && TT.tty!=slot[SLOT_ttynr]) return 0;
466 // Convert field to string representation
467 static char *string_field(struct carveup *tb, struct strawberry *field)
469 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
470 int which = field->which, sl = typos[which].slot;
471 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
473 // numbers, mostly from /proc/$PID/stat
474 if (which <= PS_BIT) {
477 if (which==PS_PRI) ll = 39-ll;
478 if (which==PS_ADDR) fmt = "%llx";
479 else if (which==PS_SZ) ll >>= 12;
480 else if (which==PS_RSS) ll <<= 2;
481 else if (which==PS_VSZ) ll >>= 10;
482 else if (which==PS_PR && ll<-9) fmt="RT";
483 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
484 sprintf(out, fmt, ll);
490 // First string slot has offset 0, others are offset[-slot-2]
491 if (--sl) out += tb->offset[--sl];
492 if (which==PS_ARGS || which==PS_COMM) {
496 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
497 if (out[i] == '/') s = out+i+1;
500 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
503 } else if (which <= PS_RGROUP) {
504 sprintf(out, "%lld", ll);
506 if (which > PS_RUSER) {
507 struct group *gr = bufgetgrgid(ll);
509 if (gr) out = gr->gr_name;
511 struct passwd *pw = bufgetpwuid(ll);
513 if (pw) out = pw->pw_name;
518 } else if (which <= PS_TIME_) {
519 int unit = 60, pad = 2, j = TT.ticks;
522 if (which!=PS_TIME_) unit *= 60*24;
524 // top adjusts slot[SLOT_upticks], we want original meaning.
525 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
528 // Output days-hours:mins:secs, skipping non-required fields with zero
529 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
530 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
531 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
533 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
535 if ((*s = "-::"[j])) s++;
540 if (which==PS_TIME_ && s-out<8)
541 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
543 // Percentage displays
544 } else if (which <= PS__CPU) {
545 ll = slot[sl&63]*1000;
546 if (which==PS__VSZ || which==PS__MEM)
547 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
548 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
550 if (which==PS_C) sl += 5;
551 sprintf(out, "%d", sl/10);
552 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
555 } else if (which <= PS_DIO) {
556 ll = slot[typos[which].slot];
557 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
558 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
559 else human_readable(out, ll, 0);
561 // Posix doesn't specify what flags should say. Man page says
562 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
563 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
564 else if (which==PS_S || which==PS_STAT) {
567 if (which==PS_STAT) {
568 // TODO l = multithreaded
569 if (slot[SLOT_nice]<0) *s++ = '<';
570 else if (slot[SLOT_nice]>0) *s++ = 'N';
571 if (slot[SLOT_sid]==*slot) *s++ = 's';
572 if (slot[SLOT_vmlck]) *s++ = 'L';
573 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
576 } else if (which==PS_STIME) {
577 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
579 // Padding behavior's a bit odd: default field size is just hh:mm.
580 // Increasing stime:size reveals more data at left until full,
581 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
582 // then add :ss on right for :19.
583 strftime(out, 260, "%F %T", localtime(&t));
584 out = out+strlen(out)-3-abs(field->len);
585 if (out<buf) out = buf;
587 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
588 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
593 // Display process data that get_ps() read from /proc, formatting with TT.fields
594 static void show_ps(void *p)
596 struct carveup *tb = p;
597 struct strawberry *field;
598 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
600 // Loop through fields to display
601 for (field = TT.fields; field; field = field->next) {
602 char *out = string_field(tb, field);
604 // Output the field, appropriately padded
606 // Minimum one space between each field
608 if (field != TT.fields) {
613 // Don't truncate number fields, but try to reclaim extra offset from later
614 // fields that can naturally be shorter
615 abslen = abs(field->len);
616 sign = field->len<0 ? -1 : 1;
617 olen = (TT.tty) ? utf8len(out) : strlen(out);
618 if ((field->which<=PS_BIT || (toys.optflags&FLAG_w)) && olen>abslen) {
619 // overflow but remember by how much
620 extra += olen-abslen;
622 } else if (extra && olen<abslen) {
623 int unused = abslen-olen;
625 // If later fields have slack space, take back overflow
626 if (unused>extra) unused = extra;
630 if (abslen>width) abslen = width;
634 // If last field is left justified, no trailing spaces.
635 if (!field->next && sign<0) {
640 // If we truncated a left-justified field, show + instead of last char
641 if (olen>len && len>1 && sign<0) {
644 if (field->next) pad++;
648 if (TT.tty) width -= draw_trim(out, pad, len);
649 else width -= printf("%*.*s", pad, len, out);
650 if (!abslen) putchar('+');
653 xputc(TT.time ? '\r' : '\n');
656 // dirtree callback: read data about process to display, store, or discard it.
657 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
658 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
659 static int get_ps(struct dirtree *new)
662 char *name; // Path under /proc/$PID directory
663 long long bits; // Only fetch extra data if an -o field is displaying it
665 // sources for carveup->offset[] data
666 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
667 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
670 struct carveup *tb = (void *)toybuf;
671 long long *slot = tb->slot;
672 char *name, *s, *buf = tb->str, *end = 0;
676 // Recurse one level into /proc children, skip non-numeric entries
678 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
679 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
681 memset(slot, 0, sizeof(tb->slot));
682 tb->slot[SLOT_tid] = *slot = atol(new->name);
683 if (TT.threadparent && TT.threadparent->extra) {
684 *slot = *(((struct carveup *)TT.threadparent->extra)->slot);
685 // Parent also shows up as a thread, discard duplicate
686 if (*slot == tb->slot[SLOT_tid]) return 0;
688 fd = dirtree_parentfd(new);
691 sprintf(buf, "%lld/stat", *slot);
692 if (!readfileat(fd, buf, buf, &len)) return 0;
694 // parse oddball fields (name and state). Name can have embedded ')' so match
695 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
696 // All remaining fields should be numeric.
697 if (!(name = strchr(buf, '('))) return 0;
698 for (s = ++name; *s; s++) if (*s == ')') end = s;
699 if (!end || end-name>255) return 0;
701 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
702 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
703 for (j = 1; j<SLOT_count; j++)
704 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
706 // Now we've read the data, move status and name right after slot[] array,
707 // and convert low chars to ? for non-tty display while we're at it.
708 for (i = 0; i<end-name; i++)
709 if ((tb->str[i] = name[i]) < ' ')
710 if (!TT.tty) tb->str[i] = '?';
713 len = sizeof(toybuf)-(buf-toybuf);
715 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
716 // or numeric wchan, and the remaining two are always zero), and vmlck into
717 // 18 (which is "obsolete, always 0" from stat)
718 slot[SLOT_uid] = new->st.st_uid;
719 slot[SLOT_gid] = new->st.st_gid;
721 // TIME and TIME+ use combined value, ksort needs 'em added.
722 slot[SLOT_utime] += slot[SLOT_stime];
723 slot[SLOT_utime2] = slot[SLOT_utime];
725 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
726 // and save ruid, rgid, and vmlck.
727 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
728 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
732 sprintf(buf, "%lld/status", *slot);
733 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
734 s = strafter(buf, "\nUid:");
735 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
736 s = strafter(buf, "\nGid:");
737 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
738 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
739 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
742 // Do we need to read "io"?
743 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
746 sprintf(buf, "%lld/io", *slot);
747 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
748 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
749 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
750 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
751 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
752 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
753 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
756 // We now know enough to skip processes we don't care about.
757 if (TT.match_process && !TT.match_process(slot)) return 0;
759 // /proc data is generated as it's read, so for maximum accuracy on slow
760 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
762 slot[SLOT_uptime] = TT.si.uptime;
763 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
765 // Do we need to read "statm"?
766 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
769 sprintf(buf, "%lld/statm", *slot);
770 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
772 for (s = buf, i=0; i<3; i++)
773 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
777 // Do we need to read "exe"?
778 if (TT.bits&_PS_BIT) {
781 sprintf(buf, "%lld/exe", *slot);
782 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
783 if (buf[4] == 1) slot[SLOT_bits] = 32;
784 else if (buf[4] == 2) slot[SLOT_bits] = 64;
788 // Do we need Android scheduling policy?
789 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
791 // Fetch string data while parentfd still available, appending to buf.
792 // (There's well over 3k of toybuf left. We could dynamically malloc, but
793 // it'd almost never get used, querying length of a proc file is awkward,
794 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
795 slot[SLOT_argv0len] = 0;
796 for (j = 0; j<ARRAY_LEN(fetch); j++) {
797 tb->offset[j] = buf-(tb->str);
798 if (!(TT.bits&fetch[j].bits)) {
803 // Determine remaining space, reserving minimum of 256 bytes/field and
804 // 260 bytes scratch space at the end (for output conversion later).
805 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
806 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
808 // For exe we readlink instead of read contents
810 struct carveup *ptb = 0;
813 // Thread doesn't have exe or argv[0], so use parent's
814 if (TT.threadparent && TT.threadparent->extra)
815 ptb = (void *)TT.threadparent->extra;
817 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
819 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
821 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
822 i = ptb->slot[SLOT_argv0len];
823 s = ptb->str+ptb->offset[4];
824 while (-1!=(k = stridx(s, '/')) && k<i) {
834 // If it's not the TTY field, data we want is in a file.
835 // Last length saved in slot[] is command line (which has embedded NULs)
837 int rdev = slot[SLOT_ttynr];
840 // Call no tty "?" rather than "0:0".
843 // Can we readlink() our way to a name?
844 for (i = 0; i<3; i++) {
845 sprintf(buf, "%lld/fd/%i", *slot, i);
846 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
847 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
851 // Couldn't find it, try all the tty drivers.
853 FILE *fp = fopen("/proc/tty/drivers", "r");
854 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
857 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
858 // TODO: we could parse the minor range too.
859 if (tty_major == maj) {
861 len += sprintf(buf+len, "%d", min);
862 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
870 // Really couldn't find it, so just show major:minor.
871 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
875 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
878 // Data we want is in a file.
879 // Last length saved in slot[] is command line (which has embedded NULs)
883 // When command has no arguments, don't space over the NUL
884 if (readfileat(fd, buf, buf, &len) && len>0) {
886 // Trim trailing whitespace and NUL bytes
888 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
891 // Turn NUL to space, other low ascii to ? (in non-tty mode)
892 // cmdline has a trailing NUL that we don't want to turn to space.
893 for (i=0; i<len-1; i++) {
899 } else if (!TT.tty && c<' ') c = '?';
902 } else *buf = len = 0;
904 // Store end of argv[0] so ARGS and CMDLINE can differ.
905 // We do it for each file string slot but last is cmdline, which sticks.
906 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
909 // Above calculated/retained len, so we don't need to re-strlen.
914 if (TT.show_process && !TT.threadparent) {
920 // If we need to sort the output, add it to the list and return.
921 s = xmalloc(buf-toybuf);
922 new->extra = (long)s;
923 memcpy(s, toybuf, buf-toybuf);
928 static int get_threads(struct dirtree *new)
932 unsigned pid, kcount;
934 if (!new->parent) return get_ps(new);
935 pid = atol(new->name);
937 TT.threadparent = new;
944 // Recurse down into tasks, retaining thread groups.
945 // Disable show_process at least until we can calculate tcount
947 sprintf(toybuf, "/proc/%u/task", pid);
948 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
949 if (new->child == DIRTREE_ABORTVAL) new->child = 0;
951 kcount = TT.kcount-kcount+1;
952 tb = (void *)new->extra;
953 tb->slot[SLOT_tcount] = kcount;
955 // Fill out tid and thread count for each entry in group
956 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
957 tb = (void *)dt->extra;
958 tb->slot[SLOT_pid] = pid;
959 tb->slot[SLOT_tcount] = kcount;
963 if (!TT.show_process) return DIRTREE_SAVE;
964 TT.show_process((void *)new->extra);
965 if ((dt = new->child)) {
968 new = dt->child->next;
969 TT.show_process((void *)dt->child->extra);
979 static char *parse_ko(void *data, char *type, int length)
981 struct strawberry *field;
982 char *width, *title, *end, *s;
985 // Get title, length of title, type, end of type, and display width
987 // Chip off =name to display
988 if ((end = strchr(type, '=')) && length>(end-type)) {
990 length -= (end-type)+1;
996 // Chip off :width to display
997 if ((width = strchr(type, ':')) && width<end) {
998 if (!title) length = width-type;
1001 // Allocate structure, copy title
1002 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
1004 memcpy(field->title = field->forever, title, length);
1005 field->title[field->len = length] = 0;
1009 field->len = strtol(++width, &title, 10);
1010 if (!isdigit(*width) || title != end) return title;
1016 if (*type == '-') field->reverse = -1;
1017 else if (*type != '+') type--;
1019 for (i = 0; i<ARRAY_LEN(typos); i++) {
1021 for (j = 0; j<2; j++) {
1022 if (!j) s = typos[i].name;
1023 // posix requires alternate names for some fields
1024 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
1025 PS_VSZ, PS_USER, 0}, i))) continue;
1027 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
1029 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
1033 if (i==ARRAY_LEN(typos)) return type;
1034 if (!field->title) field->title = typos[field->which].name;
1035 if (!field->len) field->len = typos[field->which].width;
1036 else if (typos[field->which].width<0) field->len *= -1;
1037 dlist_add_nomalloc(data, (void *)field);
1042 static long long get_headers(struct strawberry *fields, char *buf, int blen)
1047 for (; fields; fields = fields->next) {
1048 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
1050 bits |= 1LL<<fields->which;
1056 // Parse -p -s -t -u -U -g -G
1057 static char *parse_rest(void *data, char *str, int len)
1059 struct ptr_len *pl = (struct ptr_len *)data;
1064 // Allocate next chunk of data
1066 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1068 // Parse numerical input
1069 if (isdigit(*str)) {
1070 ll[pl->len] = xstrtol(str, &end, 10);
1071 if (end==(len+str)) num++;
1072 // For pkill, -s 0 represents pkill's session id.
1073 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1076 if (pl==&TT.pp || pl==&TT.ss) {
1077 if (num && ll[pl->len]>0) {
1082 } else if (pl==&TT.tt) {
1083 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1085 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1086 if (strstart(&str, "pts/")) {
1089 } else if (strstart(&str, "tty")) len -= 3;
1091 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1094 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1095 memcpy(end, str, len);
1098 ll[pl->len++] = st.st_rdev;
1102 } else if (len<255) {
1111 memcpy(name, str, len);
1113 if (pl==&TT.gg || pl==&TT.GG) {
1114 struct group *gr = getgrnam(name);
1116 ll[pl->len++] = gr->gr_gid;
1120 } else if (pl==&TT.uu || pl==&TT.UU) {
1121 struct passwd *pw = getpwnam(name);
1123 ll[pl->len++] = pw->pw_uid;
1135 static int ksort(void *aa, void *bb)
1137 struct strawberry *field;
1138 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1141 for (field = TT.kfields; field && !ret; field = field->next) {
1142 slot = typos[field->which].slot;
1144 // Can we do numeric sort?
1146 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1147 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1150 // fallback to string sort
1152 memccpy(toybuf, string_field(ta, field), 0, 2048);
1154 ret = strcmp(toybuf, string_field(tb, field));
1156 ret *= field->reverse;
1162 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1165 struct dirtree *next = dt->next;
1167 if (dt->extra) *(tb++) = (void *)dt->extra;
1168 if (dt->child) tb = collate_leaves(tb, dt->child);
1176 static struct carveup **collate(int count, struct dirtree *dt)
1178 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1180 collate_leaves(tbsort, dt);
1185 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1187 struct arg_list def;
1189 memset(&def, 0, sizeof(struct arg_list));
1191 comma_args(arg ? arg : &def, fields, err, parse_ko);
1201 TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
1203 if (-1 != (i = tty_fd())) {
1206 if (!fstat(i, &st)) TT.tty = st.st_rdev;
1209 // If we can't query terminal size pad to 80 but do -w
1211 if (!isatty(1) || !terminal_size(&TT.width, 0))
1212 toys.optflags |= FLAG_w;
1213 if (toys.optflags&FLAG_w) TT.width = 99999;
1215 // parse command line options other than -o
1216 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1217 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1218 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1219 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1220 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1221 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1222 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1223 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1224 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1225 dlist_terminate(TT.kfields);
1227 // It's undocumented, but traditionally extra arguments are extra -p args
1228 for (arg = toys.optargs; *arg; arg++)
1229 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit_raw(*arg);
1231 // Figure out which fields to display
1232 not_o = "%sTTY,TIME,CMD";
1233 if (toys.optflags&FLAG_f)
1234 sprintf(not_o = toybuf+128,
1235 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
1236 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1237 else if (toys.optflags&FLAG_l)
1238 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1239 else if (CFG_TOYBOX_ON_ANDROID)
1240 sprintf(not_o = toybuf+128,
1241 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1242 (toys.optflags&FLAG_T) ? "CMD" : "NAME");
1243 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1245 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1246 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1247 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1250 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1251 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1252 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1254 dlist_terminate(TT.fields);
1256 // -f and -n change the meaning of some fields
1257 if (toys.optflags&(FLAG_f|FLAG_n)) {
1258 struct strawberry *ever;
1260 for (ever = TT.fields; ever; ever = ever->next) {
1261 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1262 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1267 // Calculate seen fields bit array, and if we aren't deferring printing
1268 // print headers now (for low memory/nommu systems).
1269 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1270 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1271 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = show_ps;
1272 TT.match_process = ps_match_process;
1273 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1274 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1275 ? get_threads : get_ps);
1277 if ((dt != DIRTREE_ABORTVAL) && toys.optflags&(FLAG_k|FLAG_M)) {
1278 struct carveup **tbsort = collate(TT.kcount, dt);
1280 if (toys.optflags&FLAG_M) {
1281 for (i = 0; i<TT.kcount; i++) {
1282 struct strawberry *field;
1284 for (field = TT.fields; field; field = field->next) {
1285 int len = strlen(string_field(tbsort[i], field));
1287 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1291 // Now that we've recalculated field widths, re-pad headers again
1292 get_headers(TT.fields, toybuf, sizeof(toybuf));
1293 printf("%.*s\n", TT.width, toybuf);
1296 if (toys.optflags&FLAG_k)
1297 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1298 for (i = 0; i<TT.kcount; i++) {
1302 if (CFG_TOYBOX_FREE) free(tbsort);
1305 if (CFG_TOYBOX_FREE) {
1314 llist_traverse(TT.fields, free);
1320 #include "generated/flags.h"
1322 // select which of the -o fields to sort by
1323 static void setsort(int pos)
1325 struct strawberry *field, *going2;
1330 for (field = TT.fields; field; field = field->next) {
1331 if ((TT.sortpos = i++)<pos && field->next) continue;
1332 going2 = TT.kfields;
1333 going2->which = field->which;
1334 going2->len = field->len;
1339 // If we have both, adjust slot[deltas[]] to be relative to previous
1340 // measurement rather than process start. Stomping old.data is fine
1341 // because we free it after displaying.
1342 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1344 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1345 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1348 for (i = 0; i<ARRAY_LEN(deltas); i++)
1349 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1350 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1355 static int header_line(int line, int rev)
1357 if (!line) return 0;
1359 if (toys.optflags&FLAG_b) rev = 0;
1361 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1362 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1363 rev ? "\033[0m" : "");
1368 static void top_common(
1369 int (*filter)(long long *oslot, long long *nslot, int milis))
1371 long long timeout = 0, now, stats[16];
1373 struct carveup **tb;
1376 } plist[2], *plold, *plnew, old, new, mix;
1377 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1378 "iow", "irq", "sirq", "host"};
1381 int i, lines, topoff = 0, done = 0;
1383 toys.signal = SIGWINCH;
1384 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1386 memset(plist, 0, sizeof(plist));
1387 memset(stats, 0, sizeof(stats));
1392 plold = plist+(tock++&1);
1393 plnew = plist+(tock&1);
1394 plnew->whence = millitime();
1395 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1396 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1397 ? get_threads : get_ps);
1398 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
1399 plnew->tb = collate(plnew->count = TT.kcount, dt);
1402 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1403 long long *st = stats+8*(tock&1);
1405 // user nice system idle iowait irq softirq host
1406 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1407 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1410 // First time, wait a quarter of a second to collect a little delta data.
1416 // Collate old and new into "mix", depends on /proc read in pid sort order
1419 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1422 while (old.count || new.count) {
1423 struct carveup *otb = *old.tb, *ntb = *new.tb;
1425 // If we just have old for this process, it exited. Discard it.
1426 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1433 // If we just have new, use it verbatim
1434 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1437 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1438 mix.tb[mix.count] = otb;
1448 // Don't re-fetch data if it's not time yet, just re-display existing data.
1453 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1454 if (!(toys.optflags&FLAG_b)) {
1455 printf("\033[H\033[J");
1458 terminal_probesize(&TT.width, &TT.height);
1461 if (TT.top.m) TT.height = TT.top.m+5;
1464 if (recalc && !(toys.optflags&FLAG_q)) {
1465 // Display "top" header.
1466 if (*toys.which->name == 't') {
1467 struct strawberry alluc;
1468 long long ll, up = 0;
1472 // Count running, sleeping, stopped, zombie processes.
1474 memset(run, 0, sizeof(run));
1475 for (i = 0; i<mix.count; i++)
1476 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1478 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1479 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1480 lines = header_line(lines, 0);
1482 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1483 for (i=0; i<6; i++) {
1484 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1485 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1486 run[i] = pos ? atol(pos) : 0;
1489 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1490 run[0], run[0]-run[1], run[1], run[2]);
1491 lines = header_line(lines, 0);
1493 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1494 run[4], run[4]-run[5], run[5], run[3]);
1495 lines = header_line(lines, 0);
1499 i = sysconf(_SC_NPROCESSORS_CONF);
1500 pos += sprintf(pos, "%d%%cpu", i*100);
1503 // If a processor goes idle it's powered down and its idle ticks don't
1504 // advance, so calculate idle time as potential time - used.
1505 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1508 ll = stats[3] = stats[11] = 0;
1509 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1510 stats[3] = now - llabs(ll);
1512 for (i = 0; i<8; i++) {
1513 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1514 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1516 lines = header_line(lines, 0);
1518 struct strawberry *fields;
1521 memset(&tb, 0, sizeof(struct carveup));
1522 pos = stpcpy(toybuf, "Totals:");
1523 for (fields = TT.fields; fields; fields = fields->next) {
1524 long long ll, bits = 0;
1525 int slot = typos[fields->which].slot&63;
1527 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1528 ll = 1LL<<fields->which;
1529 if (bits&ll) continue;
1531 for (i=0; i<mix.count; i++)
1532 tb.slot[slot] += mix.tb[i]->slot[slot];
1533 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1534 " %s: %*s,", typos[fields->which].name,
1535 fields->len, string_field(&tb, fields));
1538 lines = header_line(lines, 0);
1541 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1542 for (i = 0, is = ' '; *pos; pos++) {
1545 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1547 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1550 lines = header_line(lines, 1);
1552 if (!recalc && !(toys.optflags&FLAG_b))
1553 printf("\033[%dH\033[J", 1+TT.height-lines);
1556 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1557 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1558 show_ps(mix.tb[i+topoff]);
1561 if (TT.top.n && !--TT.top.n) {
1567 if (timeout<=now) timeout = new.whence+TT.top.d;
1568 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1570 // In batch mode, we ignore the keyboard.
1571 if (toys.optflags&FLAG_b) {
1572 msleep(timeout-now);
1573 // Make an obvious gap between datasets.
1578 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1579 if (i==-1 || i==3 || toupper(i)=='Q') {
1584 if (i==-3) continue;
1586 // Flush unknown escape sequences.
1587 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1591 } else if (toupper(i)=='R')
1592 ((struct strawberry *)TT.kfields)->reverse *= -1;
1595 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1596 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1597 // KEY_UP is 0, so at end of strchr
1598 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1601 if (i == KEY_UP) topoff--;
1602 else if (i == KEY_DOWN) topoff++;
1603 else if (i == KEY_PGDN) topoff += lines;
1604 else if (i == KEY_PGUP) topoff -= lines;
1605 if (topoff<0) topoff = 0;
1606 if (topoff>mix.count) topoff = mix.count;
1613 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1617 if (!(toys.optflags&FLAG_b)) tty_reset();
1620 static void top_setup(char *defo, char *defk)
1624 TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
1625 TT.tty = tty_fd() != -1;
1627 // Are we doing "batch" output or interactive?
1628 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1630 // Grab starting time, make terminal raw, switch off cursor,
1631 // set signal handler to put terminal/cursor back to normal at exit.
1632 TT.time = millitime();
1633 set_terminal(0, 1, 0);
1634 sigatexit(tty_sigreset);
1635 xsignal(SIGWINCH, generic_signal);
1636 printf("\033[?25l\033[0m");
1641 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1642 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1643 TT.match_process = shared_match_process;
1645 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1646 dlist_terminate(TT.fields);
1648 // First (dummy) sort field is overwritten by setsort()
1649 default_ko("-S", &TT.kfields, 0, 0);
1650 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1651 dlist_terminate(TT.kfields);
1652 setsort(TT.top.s-1);
1657 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,%s",
1658 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1659 toys.optflags&FLAG_H ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1660 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1661 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1663 struct strawberry *fields = TT.fields;
1665 fields = fields->next->next;
1666 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1669 top_common(merge_deltas);
1674 #include "generated/flags.h"
1676 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1678 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1679 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1681 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1684 void iotop_main(void)
1686 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1688 if (toys.optflags&FLAG_K) TT.forcek++;
1690 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1691 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1694 top_common(iotop_filter);
1697 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1698 // context, so force pgrep's flags on even when building pkill standalone.
1699 // (All the pgrep/pkill functions drop out when building ps standalone.)
1701 #define CLEANUP_iotop
1703 #include "generated/flags.h"
1706 struct regex_list *next;
1710 static void do_pgk(struct carveup *tb)
1712 if (TT.pgrep.signal) {
1713 if (kill(*tb->slot, TT.pgrep.signal)) {
1714 char *s = num_to_sig(TT.pgrep.signal);
1716 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1717 perror_msg("%s->%lld", s, *tb->slot);
1720 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1721 printf("%lld", *tb->slot);
1722 if (toys.optflags&FLAG_l)
1723 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1725 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1729 static void match_pgrep(void *p)
1731 struct carveup *tb = p;
1733 struct regex_list *reg;
1734 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1736 // Never match ourselves.
1737 if (TT.pgrep.self == *tb->slot) return;
1739 if (TT.pgrep.regexes) {
1740 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1741 if (regexec(®->reg, name, 1, &match, 0)) continue;
1742 if (toys.optflags&FLAG_x)
1743 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1746 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1749 // pgrep should return success if there's a match.
1752 // Repurpose a field for -c count.
1754 if (toys.optflags&(FLAG_n|FLAG_o)) {
1755 long long ll = tb->slot[SLOT_starttime];
1757 if (toys.optflags&FLAG_o) ll *= -1;
1758 if (TT.time && TT.time>ll) return;
1760 free(TT.pgrep.snapshot);
1761 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1765 static int pgrep_match_process(long long *slot)
1767 int match = shared_match_process(slot);
1769 return (toys.optflags&FLAG_v) ? !match : match;
1772 void pgrep_main(void)
1775 struct regex_list *reg;
1777 TT.pgrep.self = getpid();
1779 // No signal names start with "L", so no need for "L: " in optstr.
1780 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1781 error_exit("bad -L '%s'", TT.pgrep.L);
1783 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1784 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1785 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1786 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1787 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1788 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1789 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1791 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1792 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1793 if (!toys.optc) help_exit("No PATTERN");
1795 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1796 for (arg = toys.optargs; *arg; arg++) {
1797 reg = xmalloc(sizeof(struct regex_list));
1798 xregcomp(®->reg, *arg, REG_EXTENDED);
1799 reg->next = TT.pgrep.regexes;
1800 TT.pgrep.regexes = reg;
1802 TT.match_process = pgrep_match_process;
1803 TT.show_process = match_pgrep;
1805 // pgrep should return failure if there are no matches.
1808 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1809 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1810 if (TT.pgrep.snapshot) {
1811 do_pgk(TT.pgrep.snapshot);
1812 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1814 if (TT.pgrep.d) xputc('\n');
1817 #define CLEANUP_pgrep
1819 #include "generated/flags.h"
1821 void pkill_main(void)
1823 char **args = toys.optargs;
1825 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1826 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1827 if (toys.optflags & FLAG_V) TT.tty = 1;