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 USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
50 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
51 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
52 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
58 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
62 Which processes to show (selections may be comma separated lists):
65 -a Processes with terminals that aren't session leaders
66 -d All processes that aren't session leaders
68 -g Belonging to GROUPs
69 -G Belonging to real GROUPs (before sgid)
71 -P Parent PIDs (--ppid)
73 -t Attached to selected TTYs
76 -U Owned by real USERs (before suid)
80 -k Sort FIELDs in +increasing or -decreasting order (--sort)
81 -M Measure field widths (expanding as necessary)
82 -n Show numeric USER and GROUP
83 -w Wide output (don't truncate fields)
85 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
87 -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
88 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
89 -o Output FIELDs instead of defaults, each with optional :size and =title
90 -O Add FIELDS to defaults
93 Command line -o fields:
95 ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
96 CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
97 COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
99 Process attribute -o FIELDs:
101 ADDR Instruction pointer BIT Is this process 32 or 64 bits
102 CPU Which processor running on ETIME Elapsed time since PID start
103 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
104 GROUP Group name LABEL Security label
105 MAJFL Major page faults MINFL Minor page faults
106 NI Niceness (lower is faster)
107 PCPU Percentage of CPU time used PCY Android scheduling policy
108 PGID Process Group ID
109 PID Process ID PPID Parent Process ID
110 PRI Priority (higher is faster) PSR Processor last executed on
111 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
112 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
113 RUID Real (before suid) user ID RUSER Real (before suid) user name
115 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
116 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
117 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
118 STAT Process state (S) plus:
119 < high priority N low priority L locked memory
120 s session leader + foreground l multithreaded
121 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
122 SZ Memory Size (4k pages needed to completely swap out process)
123 TCNT Thread count TID Thread ID
124 TIME CPU time consumed TTY Controlling terminal
125 UID User id USER User name
126 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
127 WCHAN What are we waiting in kernel for
131 depends on TOP_COMMON
134 usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
136 Show process activity in real time.
139 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
140 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
141 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
142 -s Sort by field number (1-X, default 9)
144 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
147 depends on TOP_COMMON
152 Rank processes by I/O.
154 -A All I/O, not just disk
155 -a Accumulated I/O (not percentage)
157 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
158 -O Only show processes doing I/O
159 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
160 -s Sort by field number (0-X, default 6)
166 usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
168 -b Batch mode (no tty)
169 -d Delay SECONDS between each cycle (default 3)
170 -n Exit after NUMBER iterations
173 -q Quiet (no header lines)
175 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
176 update, R to reverse sort, Q to exit.
181 depends on PGKILL_COMMON
183 usage: pgrep [-cl] [-d DELIM] [-L SIGNAL] [PATTERN]
185 Search for process(es). PATTERN is an extended regular expression checked
186 against command names.
188 -c Show only count of matches
189 -d Use DELIM instead of newline
190 -L Send SIGNAL instead of printing name
196 depends on PGKILL_COMMON
198 usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
200 -l Send SIGNAL (default SIGTERM)
207 usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
209 -f Check full command line for PATTERN
210 -G Match real Group ID(s)
211 -g Match Process Group(s) (0 is current user)
214 -P Match Parent Process ID(s)
215 -s Match Session ID(s) (0 for current)
217 -U Match real User ID(s)
218 -u Match effective User ID(s)
220 -x Match whole command (not substring)
262 void *regexes, *snapshot;
269 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
270 struct dirtree *threadparent;
271 unsigned width, height;
273 void *fields, *kfields;
274 long long ticks, bits, time;
275 int kcount, forcek, sortpos;
276 int (*match_process)(long long *slot);
277 void (*show_process)(void *tb);
281 struct strawberry *next, *prev;
282 short which, len, reverse;
287 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
288 * table 1-4) but we shift and repurpose fields, with the result being: */
291 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
292 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
293 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
294 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
295 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
296 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
297 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
298 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
299 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
300 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
301 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
302 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
303 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
304 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
305 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
306 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
307 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
308 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
309 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
310 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
311 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
312 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
313 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
314 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
315 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
316 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
317 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
318 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
319 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
320 SLOT_pcy, /*Android sched policy*/
325 // Data layout in toybuf
327 long long slot[SLOT_count]; // data (see enum above)
328 unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
330 char str[]; // name, tty, command, wchan, attr, cmdline
333 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
334 // 64|slot means compare as string when sorting
337 signed char width, slot;
338 } static const typos[] = TAGGED_ARRAY(PS,
340 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
341 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
342 {"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
343 {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
344 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
345 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
346 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
349 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
350 {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
351 {"ARGS", -27, -6}, {"CMD", -15, -1},
354 {"UID", 5, SLOT_uid}, {"USER", -12, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
355 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
356 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
359 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
360 {"TIME+", 9, SLOT_utime},
362 // Percentage displays
363 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
364 {"%CPU", 4, SLOT_utime2},
367 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
368 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
369 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
370 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
373 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
374 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
377 // Return 0 to discard, nonzero to keep
378 static int shared_match_process(long long *slot)
380 struct ptr_len match[] = {
381 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
382 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
383 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
388 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
389 for (i = 0; i < ARRAY_LEN(match); i++) {
390 struct ptr_len *mm = match[i].ptr;
394 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
402 // Return 0 to discard, nonzero to keep
403 static int ps_match_process(long long *slot)
405 int i = shared_match_process(slot);
408 // If we had selections and didn't match them, don't display
411 // Filter implicit categories for other display types
412 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
413 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
414 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
415 && TT.tty!=slot[SLOT_ttynr]) return 0;
420 // Convert field to string representation
421 static char *string_field(struct carveup *tb, struct strawberry *field)
423 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
424 int which = field->which, sl = typos[which].slot;
425 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
427 // numbers, mostly from /proc/$PID/stat
428 if (which <= PS_BIT) {
431 if (which==PS_PRI) ll = 39-ll;
432 if (which==PS_ADDR) fmt = "%llx";
433 else if (which==PS_SZ) ll >>= 12;
434 else if (which==PS_RSS) ll <<= 2;
435 else if (which==PS_VSZ) ll >>= 10;
436 else if (which==PS_PR && ll<-9) fmt="RT";
437 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
438 sprintf(out, fmt, ll);
444 // First string slot has offset 0, others are offset[-slot-2]
445 if (--sl) out += tb->offset[--sl];
446 if (which==PS_ARGS || which==PS_COMM) {
450 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
451 if (out[i] == '/') s = out+i+1;
454 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
457 } else if (which <= PS_RGROUP) {
458 sprintf(out, "%lld", ll);
460 if (which > PS_RUSER) {
461 struct group *gr = bufgetgrgid(ll);
463 if (gr) out = gr->gr_name;
465 struct passwd *pw = bufgetpwuid(ll);
467 if (pw) out = pw->pw_name;
472 } else if (which <= PS_TIME_) {
473 int unit = 60, pad = 2, j = TT.ticks;
476 if (which!=PS_TIME_) unit *= 60*24;
478 // top adjusts slot[SLOT_upticks], we want original meaning.
479 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
482 // Output days-hours:mins:secs, skipping non-required fields with zero
483 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
484 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
485 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
487 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
489 if ((*s = "-::"[j])) s++;
494 if (which==PS_TIME_ && s-out<8)
495 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
497 // Percentage displays
498 } else if (which <= PS__CPU) {
499 ll = slot[sl&63]*1000;
500 if (which==PS__VSZ || which==PS__MEM)
501 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
502 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
504 if (which==PS_C) sl += 5;
505 sprintf(out, "%d", sl/10);
506 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
509 } else if (which <= PS_DIO) {
510 ll = slot[typos[which].slot];
511 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
512 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
513 else human_readable(out, ll, 0);
515 // Posix doesn't specify what flags should say. Man page says
516 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
517 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
518 else if (which==PS_S || which==PS_STAT) {
521 if (which==PS_STAT) {
522 // TODO l = multithreaded
523 if (slot[SLOT_nice]<0) *s++ = '<';
524 else if (slot[SLOT_nice]>0) *s++ = 'N';
525 if (slot[SLOT_sid]==*slot) *s++ = 's';
526 if (slot[SLOT_vmlck]) *s++ = 'L';
527 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
530 } else if (which==PS_STIME) {
531 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
533 // Padding behavior's a bit odd: default field size is just hh:mm.
534 // Increasing stime:size reveals more data at left until full,
535 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
536 // then add :ss on right for :19.
537 strftime(out, 260, "%F %T", localtime(&t));
538 out = out+strlen(out)-3-abs(field->len);
539 if (out<buf) out = buf;
541 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
542 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
547 // Display process data that get_ps() read from /proc, formatting with TT.fields
548 static void show_ps(void *p)
550 struct carveup *tb = p;
551 struct strawberry *field;
552 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
554 // Loop through fields to display
555 for (field = TT.fields; field; field = field->next) {
556 char *out = string_field(tb, field);
558 // Output the field, appropriately padded
560 // Minimum one space between each field
561 if (field != TT.fields) {
566 // Don't truncate number fields, but try to reclaim extra offset from later
567 // fields that can naturally be shorter
568 abslen = abs(field->len);
569 sign = field->len<0 ? -1 : 1;
570 olen = (TT.tty) ? utf8len(out) : strlen(out);
571 if ((field->which<=PS_BIT || (toys.optflags&FLAG_w)) && olen>abslen) {
572 // overflow but remember by how much
573 extra += olen-abslen;
575 } else if (extra && olen<abslen) {
576 int unused = abslen-olen;
578 // If later fields have slack space, take back overflow
579 if (unused>extra) unused = extra;
583 if (abslen>width) abslen = width;
587 // If last field is left justified, no trailing spaces.
588 if (!field->next && sign<0) pad = -1;
590 // If we truncated a left-justified field, show + instead of last char
591 if (olen>len && len>1 && sign<0) {
594 if (field->next) pad++;
598 if (TT.tty) width -= draw_trim(out, pad, len);
599 else width -= printf("%*.*s", pad, len, out);
600 if (!abslen) putchar('+');
603 xputc(TT.time ? '\r' : '\n');
606 // dirtree callback: read data about process to display, store, or discard it.
607 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
608 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
609 static int get_ps(struct dirtree *new)
612 char *name; // Path under /proc/$PID directory
613 long long bits; // Only fetch extra data if an -o field is displaying it
615 // sources for carveup->offset[] data
616 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
617 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
620 struct carveup *tb = (void *)toybuf;
621 long long *slot = tb->slot;
622 char *name, *s, *buf = tb->str, *end = 0;
626 // Recurse one level into /proc children, skip non-numeric entries
628 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
629 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
631 memset(slot, 0, sizeof(tb->slot));
632 tb->slot[SLOT_tid] = *slot = atol(new->name);
633 if (TT.threadparent && TT.threadparent->extra)
634 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
635 fd = dirtree_parentfd(new);
638 sprintf(buf, "%lld/stat", *slot);
639 if (!readfileat(fd, buf, buf, &len)) return 0;
641 // parse oddball fields (name and state). Name can have embedded ')' so match
642 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
643 // All remaining fields should be numeric.
644 if (!(name = strchr(buf, '('))) return 0;
645 for (s = ++name; *s; s++) if (*s == ')') end = s;
646 if (!end || end-name>255) return 0;
648 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
649 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
650 for (j = 1; j<SLOT_count; j++)
651 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
653 // Now we've read the data, move status and name right after slot[] array,
654 // and convert low chars to ? for non-tty display while we're at it.
655 for (i = 0; i<end-name; i++)
656 if ((tb->str[i] = name[i]) < ' ')
657 if (!TT.tty) tb->str[i] = '?';
660 len = sizeof(toybuf)-(buf-toybuf);
662 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
663 // or numeric wchan, and the remaining two are always zero), and vmlck into
664 // 18 (which is "obsolete, always 0" from stat)
665 slot[SLOT_uid] = new->st.st_uid;
666 slot[SLOT_gid] = new->st.st_gid;
668 // TIME and TIME+ use combined value, ksort needs 'em added.
669 slot[SLOT_utime] += slot[SLOT_stime];
670 slot[SLOT_utime2] = slot[SLOT_utime];
672 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
673 // and save ruid, rgid, and vmlck.
674 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
675 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
679 sprintf(buf, "%lld/status", *slot);
680 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
681 s = strafter(buf, "\nUid:");
682 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
683 s = strafter(buf, "\nGid:");
684 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
685 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
686 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
689 // Do we need to read "io"?
690 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
693 sprintf(buf, "%lld/io", *slot);
694 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
695 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
696 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
697 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
698 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
699 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
700 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
703 // We now know enough to skip processes we don't care about.
704 if (TT.match_process && !TT.match_process(slot)) return 0;
706 // /proc data is generated as it's read, so for maximum accuracy on slow
707 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
709 slot[SLOT_uptime] = TT.si.uptime;
710 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
712 // Do we need to read "statm"?
713 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
716 sprintf(buf, "%lld/statm", *slot);
717 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
719 for (s = buf, i=0; i<3; i++)
720 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
724 // Do we need to read "exe"?
725 if (TT.bits&_PS_BIT) {
728 sprintf(buf, "%lld/exe", *slot);
729 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
730 if (buf[4] == 1) slot[SLOT_bits] = 32;
731 else if (buf[4] == 2) slot[SLOT_bits] = 64;
735 // Do we need Android scheduling policy?
736 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
738 // Fetch string data while parentfd still available, appending to buf.
739 // (There's well over 3k of toybuf left. We could dynamically malloc, but
740 // it'd almost never get used, querying length of a proc file is awkward,
741 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
742 slot[SLOT_argv0len] = 0;
743 for (j = 0; j<ARRAY_LEN(fetch); j++) {
744 tb->offset[j] = buf-(tb->str);
745 if (!(TT.bits&fetch[j].bits)) {
750 // Determine remaining space, reserving minimum of 256 bytes/field and
751 // 260 bytes scratch space at the end (for output conversion later).
752 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
753 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
755 // For exe we readlink instead of read contents
757 struct carveup *ptb = 0;
760 // Thread doesn't have exe or argv[0], so use parent's
761 if (TT.threadparent && TT.threadparent->extra)
762 ptb = (void *)TT.threadparent->extra;
764 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
766 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
768 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
769 i = ptb->slot[SLOT_argv0len];
770 s = ptb->str+ptb->offset[4];
771 while (-1!=(k = stridx(s, '/')) && k<i) {
781 // If it's not the TTY field, data we want is in a file.
782 // Last length saved in slot[] is command line (which has embedded NULs)
784 int rdev = slot[SLOT_ttynr];
787 // Call no tty "?" rather than "0:0".
790 // Can we readlink() our way to a name?
791 for (i = 0; i<3; i++) {
792 sprintf(buf, "%lld/fd/%i", *slot, i);
793 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
794 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
798 // Couldn't find it, try all the tty drivers.
800 FILE *fp = fopen("/proc/tty/drivers", "r");
801 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
804 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
805 // TODO: we could parse the minor range too.
806 if (tty_major == maj) {
808 len += sprintf(buf+len, "%d", min);
809 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
817 // Really couldn't find it, so just show major:minor.
818 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
822 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
825 // Data we want is in a file.
826 // Last length saved in slot[] is command line (which has embedded NULs)
830 // When command has no arguments, don't space over the NUL
831 if (readfileat(fd, buf, buf, &len) && len>0) {
833 // Trim trailing whitespace and NUL bytes
835 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
838 // Turn NUL to space, other low ascii to ? (in non-tty mode)
839 // cmdline has a trailing NUL that we don't want to turn to space.
840 for (i=0; i<len-1; i++) {
846 } else if (!TT.tty && c<' ') c = '?';
849 } else *buf = len = 0;
851 // Store end of argv[0] so ARGS and CMDLINE can differ.
852 // We do it for each file string slot but last is cmdline, which sticks.
853 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
856 // Above calculated/retained len, so we don't need to re-strlen.
861 if (TT.show_process && !TT.threadparent) {
867 // If we need to sort the output, add it to the list and return.
868 s = xmalloc(buf-toybuf);
869 new->extra = (long)s;
870 memcpy(s, toybuf, buf-toybuf);
875 static int get_threads(struct dirtree *new)
879 unsigned pid, kcount;
881 if (!new->parent) return get_ps(new);
882 pid = atol(new->name);
884 TT.threadparent = new;
891 // Recurse down into tasks, retaining thread groups.
892 // Disable show_process at least until we can calculate tcount
894 sprintf(toybuf, "/proc/%u/task", pid);
895 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
897 kcount = TT.kcount-kcount+1;
898 tb = (void *)new->extra;
899 tb->slot[SLOT_tcount] = kcount;
901 // Fill out tid and thread count for each entry in group
902 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
903 tb = (void *)dt->extra;
904 tb->slot[SLOT_pid] = pid;
905 tb->slot[SLOT_tcount] = kcount;
909 if (!TT.show_process) return DIRTREE_SAVE;
910 TT.show_process((void *)new->extra);
914 new = dt->child->next;
915 TT.show_process((void *)dt->child->extra);
924 static char *parse_ko(void *data, char *type, int length)
926 struct strawberry *field;
927 char *width, *title, *end, *s;
930 // Get title, length of title, type, end of type, and display width
932 // Chip off =name to display
933 if ((end = strchr(type, '=')) && length>(end-type)) {
935 length -= (end-type)+1;
941 // Chip off :width to display
942 if ((width = strchr(type, ':')) && width<end) {
943 if (!title) length = width-type;
946 // Allocate structure, copy title
947 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
949 memcpy(field->title = field->forever, title, length);
950 field->title[field->len = length] = 0;
954 field->len = strtol(++width, &title, 10);
955 if (!isdigit(*width) || title != end) return title;
961 if (*type == '-') field->reverse = -1;
962 else if (*type != '+') type--;
964 for (i = 0; i<ARRAY_LEN(typos); i++) {
966 for (j = 0; j<2; j++) {
967 if (!j) s = typos[i].name;
968 // posix requires alternate names for some fields
969 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
970 PS_VSZ, PS_USER, 0}, i))) continue;
972 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
974 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
978 if (i==ARRAY_LEN(typos)) return type;
979 if (!field->title) field->title = typos[field->which].name;
980 if (!field->len) field->len = typos[field->which].width;
981 else if (typos[field->which].width<0) field->len *= -1;
982 dlist_add_nomalloc(data, (void *)field);
987 static long long get_headers(struct strawberry *fields, char *buf, int blen)
992 for (; fields; fields = fields->next) {
993 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
995 bits |= 1LL<<fields->which;
1001 // Parse -p -s -t -u -U -g -G
1002 static char *parse_rest(void *data, char *str, int len)
1004 struct ptr_len *pl = (struct ptr_len *)data;
1009 // Allocate next chunk of data
1011 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1013 // Parse numerical input
1014 if (isdigit(*str)) {
1015 ll[pl->len] = xstrtol(str, &end, 10);
1016 if (end==(len+str)) num++;
1017 // For pkill, -s 0 represents pkill's session id.
1018 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1021 if (pl==&TT.pp || pl==&TT.ss) {
1022 if (num && ll[pl->len]>0) {
1027 } else if (pl==&TT.tt) {
1028 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1030 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1031 if (strstart(&str, "pts/")) {
1034 } else if (strstart(&str, "tty")) len -= 3;
1036 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1039 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1040 memcpy(end, str, len);
1043 ll[pl->len++] = st.st_rdev;
1047 } else if (len<255) {
1056 memcpy(name, str, len);
1058 if (pl==&TT.gg || pl==&TT.GG) {
1059 struct group *gr = getgrnam(name);
1061 ll[pl->len++] = gr->gr_gid;
1065 } else if (pl==&TT.uu || pl==&TT.UU) {
1066 struct passwd *pw = getpwnam(name);
1068 ll[pl->len++] = pw->pw_uid;
1080 static int ksort(void *aa, void *bb)
1082 struct strawberry *field;
1083 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1086 for (field = TT.kfields; field && !ret; field = field->next) {
1087 slot = typos[field->which].slot;
1089 // Can we do numeric sort?
1091 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1092 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1095 // fallback to string sort
1097 memccpy(toybuf, string_field(ta, field), 0, 2048);
1099 ret = strcmp(toybuf, string_field(tb, field));
1101 ret *= field->reverse;
1107 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1110 struct dirtree *next = dt->next;
1112 if (dt->extra) *(tb++) = (void *)dt->extra;
1113 if (dt->child) tb = collate_leaves(tb, dt->child);
1121 static struct carveup **collate(int count, struct dirtree *dt)
1123 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1125 collate_leaves(tbsort, dt);
1130 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1132 struct arg_list def;
1134 memset(&def, 0, sizeof(struct arg_list));
1136 comma_args(arg ? arg : &def, fields, err, parse_ko);
1139 static void shared_main(void)
1143 TT.ticks = sysconf(_SC_CLK_TCK);
1147 // If ps can't query terminal size pad to 80 but do -w
1148 if (!terminal_size(&TT.width, &TT.height) && toys.which->name[1] == 's')
1149 toys.optflags |= FLAG_w;
1152 // find controlling tty, falling back to /dev/tty if none
1153 for (i = 0; !TT.tty && i<4; i++) {
1157 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1159 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1160 if (i==3) close(fd);
1172 if (toys.optflags&FLAG_w) TT.width = 99999;
1174 // parse command line options other than -o
1175 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1176 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1177 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1178 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1179 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1180 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1181 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1182 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1183 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1184 dlist_terminate(TT.kfields);
1186 // It's undocumented, but traditionally extra arguments are extra -p args
1187 for (arg = toys.optargs; *arg; arg++)
1188 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit_raw(*arg);
1190 // Figure out which fields to display
1191 not_o = "%sTTY,TIME,CMD";
1192 if (toys.optflags&FLAG_f)
1193 sprintf(not_o = toybuf+128,
1194 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
1195 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1196 else if (toys.optflags&FLAG_l)
1197 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1198 else if (CFG_TOYBOX_ON_ANDROID)
1199 sprintf(not_o = toybuf+128,
1200 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1201 (toys.optflags&FLAG_T) ? "CMD" : "NAME");
1202 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1204 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1205 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1206 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1209 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1210 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1211 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1213 dlist_terminate(TT.fields);
1215 // -f and -n change the meaning of some fields
1216 if (toys.optflags&(FLAG_f|FLAG_n)) {
1217 struct strawberry *ever;
1219 for (ever = TT.fields; ever; ever = ever->next) {
1220 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1221 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1226 // Calculate seen fields bit array, and if we aren't deferring printing
1227 // print headers now (for low memory/nommu systems).
1228 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1229 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1230 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = show_ps;
1231 TT.match_process = ps_match_process;
1232 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1233 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1234 ? get_threads : get_ps);
1236 if (toys.optflags&(FLAG_k|FLAG_M)) {
1237 struct carveup **tbsort = collate(TT.kcount, dt);
1239 if (toys.optflags&FLAG_M) {
1240 for (i = 0; i<TT.kcount; i++) {
1241 struct strawberry *field;
1243 for (field = TT.fields; field; field = field->next) {
1244 int len = strlen(string_field(tbsort[i], field));
1246 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1250 // Now that we've recalculated field widths, re-pad headers again
1251 get_headers(TT.fields, toybuf, sizeof(toybuf));
1252 printf("%.*s\n", TT.width, toybuf);
1255 if (toys.optflags&FLAG_k)
1256 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1257 for (i = 0; i<TT.kcount; i++) {
1261 if (CFG_TOYBOX_FREE) free(tbsort);
1264 if (CFG_TOYBOX_FREE) {
1273 llist_traverse(TT.fields, free);
1279 #include "generated/flags.h"
1281 // select which of the -o fields to sort by
1282 static void setsort(int pos)
1284 struct strawberry *field, *going2;
1289 for (field = TT.fields; field; field = field->next) {
1290 if ((TT.sortpos = i++)<pos && field->next) continue;
1291 going2 = TT.kfields;
1292 going2->which = field->which;
1293 going2->len = field->len;
1298 // If we have both, adjust slot[deltas[]] to be relative to previous
1299 // measurement rather than process start. Stomping old.data is fine
1300 // because we free it after displaying.
1301 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1303 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1304 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1307 for (i = 0; i<ARRAY_LEN(deltas); i++)
1308 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1309 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1314 static int header_line(int line, int rev)
1316 if (!line) return 0;
1318 if (toys.optflags&FLAG_b) rev = 0;
1320 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1321 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1322 rev ? "\033[0m" : "");
1327 static long long millitime(void)
1331 clock_gettime(CLOCK_MONOTONIC, &ts);
1332 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1335 static void top_common(
1336 int (*filter)(long long *oslot, long long *nslot, int milis))
1338 long long timeout = 0, now, stats[16];
1340 struct carveup **tb;
1343 } plist[2], *plold, *plnew, old, new, mix;
1344 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1345 "iow", "irq", "sirq", "host"};
1348 int i, lines, topoff = 0, done = 0;
1350 toys.signal = SIGWINCH;
1351 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1353 memset(plist, 0, sizeof(plist));
1354 memset(stats, 0, sizeof(stats));
1359 plold = plist+(tock++&1);
1360 plnew = plist+(tock&1);
1361 plnew->whence = millitime();
1362 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1363 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1364 ? get_threads : get_ps);
1365 plnew->tb = collate(plnew->count = TT.kcount, dt);
1368 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1369 long long *st = stats+8*(tock&1);
1371 // user nice system idle iowait irq softirq host
1372 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1373 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1376 // First time, wait a quarter of a second to collect a little delta data.
1382 // Collate old and new into "mix", depends on /proc read in pid sort order
1385 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1388 while (old.count || new.count) {
1389 struct carveup *otb = *old.tb, *ntb = *new.tb;
1391 // If we just have old for this process, it exited. Discard it.
1392 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1399 // If we just have new, use it verbatim
1400 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1403 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1404 mix.tb[mix.count] = otb;
1414 // Don't re-fetch data if it's not time yet, just re-display existing data.
1419 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1420 if (!(toys.optflags&FLAG_b)) {
1421 printf("\033[H\033[J");
1424 terminal_probesize(&TT.width, &TT.height);
1429 if (recalc && !(toys.optflags&FLAG_q)) {
1430 // Display "top" header.
1431 if (*toys.which->name == 't') {
1432 struct strawberry alluc;
1433 long long ll, up = 0;
1437 // Count running, sleeping, stopped, zombie processes.
1439 memset(run, 0, sizeof(run));
1440 for (i = 0; i<mix.count; i++)
1441 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1443 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1444 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1445 lines = header_line(lines, 0);
1447 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1448 for (i=0; i<6; i++) {
1449 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1450 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1451 run[i] = pos ? atol(pos) : 0;
1454 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1455 run[0], run[0]-run[1], run[1], run[2]);
1456 lines = header_line(lines, 0);
1458 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1459 run[4], run[4]-run[5], run[5], run[3]);
1460 lines = header_line(lines, 0);
1464 i = sysconf(_SC_NPROCESSORS_CONF);
1465 pos += sprintf(pos, "%d%%cpu", i*100);
1468 // If a processor goes idle it's powered down and its idle ticks don't
1469 // advance, so calculate idle time as potential time - used.
1470 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1473 ll = stats[3] = stats[11] = 0;
1474 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1475 stats[3] = now - llabs(ll);
1477 for (i = 0; i<8; i++) {
1478 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1479 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1481 lines = header_line(lines, 0);
1483 struct strawberry *fields;
1486 memset(&tb, 0, sizeof(struct carveup));
1487 pos = stpcpy(toybuf, "Totals:");
1488 for (fields = TT.fields; fields; fields = fields->next) {
1489 long long ll, bits = 0;
1490 int slot = typos[fields->which].slot&63;
1492 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1493 ll = 1LL<<fields->which;
1494 if (bits&ll) continue;
1496 for (i=0; i<mix.count; i++)
1497 tb.slot[slot] += mix.tb[i]->slot[slot];
1498 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1499 " %s: %*s,", typos[fields->which].name,
1500 fields->len, string_field(&tb, fields));
1503 lines = header_line(lines, 0);
1506 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1507 for (i = 0, is = ' '; *pos; pos++) {
1510 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1512 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1515 lines = header_line(lines, 1);
1517 if (!recalc && !(toys.optflags&FLAG_b))
1518 printf("\033[%dH\033[J", 1+TT.height-lines);
1521 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1522 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1523 show_ps(mix.tb[i+topoff]);
1526 if (TT.top.n && !--TT.top.n) {
1532 if (timeout<=now) timeout = new.whence+TT.top.d;
1533 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1535 // In batch mode, we ignore the keyboard.
1536 if (toys.optflags&FLAG_b) {
1537 msleep(timeout-now);
1538 // Make an obvious gap between datasets.
1543 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1544 if (i==-1 || i==3 || toupper(i)=='Q') {
1549 if (i==-3) continue;
1551 // Flush unknown escape sequences.
1552 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1556 } else if (toupper(i)=='R')
1557 ((struct strawberry *)TT.kfields)->reverse *= -1;
1560 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1561 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1562 // KEY_UP is 0, so at end of strchr
1563 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1566 if (i == KEY_UP) topoff--;
1567 else if (i == KEY_DOWN) topoff++;
1568 else if (i == KEY_PGDN) topoff += lines;
1569 else if (i == KEY_PGUP) topoff -= lines;
1570 if (topoff<0) topoff = 0;
1571 if (topoff>mix.count) topoff = mix.count;
1578 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1582 if (!(toys.optflags&FLAG_b)) tty_reset();
1585 static void top_setup(char *defo, char *defk)
1588 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1590 TT.time = millitime();
1591 set_terminal(0, 1, 0);
1592 sigatexit(tty_sigreset);
1593 xsignal(SIGWINCH, generic_signal);
1594 printf("\033[?25l\033[0m");
1598 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1599 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1600 TT.match_process = shared_match_process;
1602 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1603 dlist_terminate(TT.fields);
1605 // First (dummy) sort field is overwritten by setsort()
1606 default_ko("-S", &TT.kfields, 0, 0);
1607 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1608 dlist_terminate(TT.kfields);
1609 setsort(TT.top.s-1);
1614 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,%s",
1615 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1616 toys.optflags&FLAG_H ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1617 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1618 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1620 struct strawberry *fields = TT.fields;
1622 fields = fields->next->next;
1623 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1626 top_common(merge_deltas);
1631 #include "generated/flags.h"
1633 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1635 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1636 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1638 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1641 void iotop_main(void)
1643 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1645 if (toys.optflags&FLAG_K) TT.forcek++;
1647 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1648 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1651 top_common(iotop_filter);
1654 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1655 // context, so force pgrep's flags on even when building pkill standalone.
1656 // (All the pgrep/pkill functions drop out when building ps standalone.)
1658 #define CLEANUP_iotop
1660 #include "generated/flags.h"
1663 struct regex_list *next;
1667 static void do_pgk(struct carveup *tb)
1669 if (TT.pgrep.signal) {
1670 if (kill(*tb->slot, TT.pgrep.signal)) {
1671 char *s = num_to_sig(TT.pgrep.signal);
1673 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1674 perror_msg("%s->%lld", s, *tb->slot);
1677 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1678 printf("%lld", *tb->slot);
1679 if (toys.optflags&FLAG_l)
1680 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1682 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1686 static void match_pgrep(void *p)
1688 struct carveup *tb = p;
1690 struct regex_list *reg;
1691 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1693 // Never match ourselves.
1694 if (TT.pgrep.self == *tb->slot) return;
1696 if (TT.pgrep.regexes) {
1697 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1698 if (regexec(®->reg, name, 1, &match, 0)) continue;
1699 if (toys.optflags&FLAG_x)
1700 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1703 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1706 // pgrep should return success if there's a match.
1709 // Repurpose a field for -c count.
1711 if (toys.optflags&(FLAG_n|FLAG_o)) {
1712 long long ll = tb->slot[SLOT_starttime];
1714 if (toys.optflags&FLAG_o) ll *= -1;
1715 if (TT.time && TT.time>ll) return;
1717 free(TT.pgrep.snapshot);
1718 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1722 static int pgrep_match_process(long long *slot)
1724 int match = shared_match_process(slot);
1726 return (toys.optflags&FLAG_v) ? !match : match;
1729 void pgrep_main(void)
1732 struct regex_list *reg;
1734 TT.pgrep.self = getpid();
1736 // No signal names start with "L", so no need for "L: " parsing.
1737 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1738 error_exit("bad -L '%s'", TT.pgrep.L);
1740 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1741 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1742 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1743 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1744 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1745 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1746 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1748 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1749 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1750 if (!toys.optc) help_exit("No PATTERN");
1752 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1753 for (arg = toys.optargs; *arg; arg++) {
1754 reg = xmalloc(sizeof(struct regex_list));
1755 xregcomp(®->reg, *arg, REG_EXTENDED);
1756 reg->next = TT.pgrep.regexes;
1757 TT.pgrep.regexes = reg;
1759 TT.match_process = pgrep_match_process;
1760 TT.show_process = match_pgrep;
1762 // pgrep should return failure if there are no matches.
1765 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1766 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1767 if (TT.pgrep.snapshot) {
1768 do_pgk(TT.pgrep.snapshot);
1769 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1771 if (TT.pgrep.d) xputc('\n');
1774 #define CLEANUP_pgrep
1776 #include "generated/flags.h"
1778 void pkill_main(void)
1780 char **args = toys.optargs;
1782 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1783 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1784 if (toys.optflags & FLAG_V) TT.tty = 1;