1 /* ps.c - show process list
3 * Copyright 2015 Rob Landley <rob@landley.net>
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
6 * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
7 * And linux kernel source fs/proc/array.c function do_task_stat()
9 * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to
10 * mean "show numeric users and groups" instead.
11 * Posix says default output should have field named "TTY" but if you "-o tty"
12 * the same field should be called "TT" which is _INSANE_ and I'm not doing it.
13 * Similarly -f outputs USER but calls it UID (we call it USER).
14 * It also says that -o "args" and "comm" should behave differently but use
15 * the same title, which is not the same title as the default output. (No.)
16 * Select by session id is -s not -g.
18 * Posix defines -o ADDR as "The address of the process" but the process
19 * start address is a constant on any elf system with mmu. The procps ADDR
20 * field always prints "-" with an alignment of 1, which is why it has 11
21 * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you
22 * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't
23 * be sanely implemented on 64 bit Linux systems. In procps there's ps -y
24 * which changes -l by removing the "F" column and swapping RSS for ADDR,
25 * leaving 9 chars for cmd, so we're using that as our -l output.
27 * Added a bunch of new -o fields posix doesn't mention, and we don't
28 * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't
29 * output argv[0] unmodified for -o comm or -o args (but procps violates
30 * posix for -o comm anyway, it's stat[2] not argv[0]).
32 * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
33 * files (why they're not globally readable when the rest of proc
34 * data is...?) and get a global I/O picture. Normal top is NOT,
35 * even though you can -o AIO there, to give sysadmins the option
36 * to reduce security exposure.)
38 * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
39 * TODO: switch -fl to -y
40 * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
41 * TODO: iotop: Window size change: respond immediately. Why not padding
42 * at right edge? (Not adjusting to screen size at all? Header wraps?)
43 * TODO: top: thread support and SMP
44 * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
46 USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
47 // stayroot because iotop needs root to read other process' proc/$$/io
48 USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
49 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
50 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
51 USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
57 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
61 Which processes to show (selections may be comma separated lists):
64 -a Processes with terminals that aren't session leaders
65 -d All processes that aren't session leaders
67 -g Belonging to GROUPs
68 -G Belonging to real GROUPs (before sgid)
70 -P Parent PIDs (--ppid)
72 -t Attached to selected TTYs
75 -U Owned by real USERs (before suid)
79 -k Sort FIELDs in +increasing or -decreasting order (--sort)
80 -M Measure field widths (expanding as necessary)
81 -n Show numeric USER and GROUP
82 -w Wide output (don't truncate at terminal width)
84 Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
86 -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
87 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
88 -o Output FIELDs instead of defaults, each with optional :size and =title
89 -O Add FIELDS to defaults
92 Command line -o fields:
94 ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
95 CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
96 COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
98 Process attribute -o FIELDs:
100 ADDR Instruction pointer BIT Is this process 32 or 64 bits
101 CPU Which processor running on ETIME Elapsed time since PID start
102 F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
103 GROUP Group name LABEL Security label
104 MAJFL Major page faults MINFL Minor page faults
105 NI Niceness (lower is faster)
106 PCPU Percentage of CPU time used PCY Android scheduling policy
107 PGID Process Group ID
108 PID Process ID PPID Parent Process ID
109 PRI Priority (higher is faster) PSR Processor last executed on
110 RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
111 RSS Resident Set Size (pages in use) RTPRIO Realtime priority
112 RUID Real (before suid) user ID RUSER Real (before suid) user name
114 R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
115 Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
116 SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
117 STAT Process state (S) plus:
118 < high priority N low priority L locked memory
119 s session leader + foreground l multithreaded
120 STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
121 SZ Memory Size (4k pages needed to completely swap out process)
122 TCNT Thread count TID Thread ID
123 TIME CPU time consumed TTY Controlling terminal
124 UID User id USER User name
125 VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
126 WCHAN What are we waiting in kernel for
130 depends on TOP_COMMON
133 usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
135 Show process activity in real time.
138 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
139 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
140 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
141 -s Sort by field number (1-X, default 9)
143 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
146 depends on TOP_COMMON
151 Rank processes by I/O.
153 -A All I/O, not just disk
154 -a Accumulated I/O (not percentage)
156 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
157 -O Only show processes doing I/O
158 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
159 -s Sort by field number (0-X, default 6)
165 usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
167 -b Batch mode (no tty)
168 -d Delay SECONDS between each cycle (default 3)
169 -n Exit after NUMBER iterations
172 -q Quiet (no header lines)
174 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
175 update, R to reverse sort, Q to exit.
180 depends on PGKILL_COMMON
182 usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]
184 Search for process(es). PATTERN is an extended regular expression checked
185 against command names.
187 -c Show only count of matches
188 -d Use DELIM instead of newline
189 -L Send SIGNAL instead of printing name
195 depends on PGKILL_COMMON
197 usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
199 -l Send SIGNAL (default SIGTERM)
206 usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
208 -f Check full command line for PATTERN
209 -G Match real Group ID(s)
210 -g Match Process Group(s) (0 is current user)
213 -P Match Parent Process ID(s)
214 -s Match Session ID(s) (0 for current)
216 -U Match real User ID(s)
217 -u Match effective User ID(s)
219 -x Match whole command (not substring)
261 void *regexes, *snapshot;
268 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
269 struct dirtree *threadparent;
270 unsigned width, height;
272 void *fields, *kfields;
273 long long ticks, bits, time;
274 int kcount, forcek, sortpos;
275 int (*match_process)(long long *slot);
276 void (*show_process)(void *tb);
280 struct strawberry *next, *prev;
281 short which, len, reverse;
286 /* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
287 * table 1-4) but we shift and repurpose fields, with the result being: */
290 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
291 SLOT_pgrp, /*process group*/ SLOT_sid, // session id
292 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty
293 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
294 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
295 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
296 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
297 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
298 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
299 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
300 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size
301 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr
302 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address
303 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer
304 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes
305 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id
306 SLOT_ruid, /*real user id*/ SLOT_gid, // group id
307 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent
308 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority
309 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
310 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
311 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
312 SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
313 SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
314 SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
315 SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
316 SLOT_rbytes, /*Disk bytes read*/ SLOT_wbytes, // Disk bytes written
317 SLOT_swap, /*Swap pages used*/ SLOT_bits, // 32 or 64
318 SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
319 SLOT_pcy, /*Android sched policy*/
324 // Data layout in toybuf
326 long long slot[SLOT_count]; // data (see enum above)
327 unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
329 char str[]; // name, tty, command, wchan, attr, cmdline
332 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
333 // 64|slot means compare as string when sorting
336 signed char width, slot;
337 } static const typos[] = TAGGED_ARRAY(PS,
339 {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
340 {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
341 {"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
342 {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
343 {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
344 {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
345 {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
348 {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
349 {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
350 {"ARGS", -27, -6}, {"CMD", -15, -1},
353 {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
354 {"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
355 {"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
358 {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
359 {"TIME+", 9, SLOT_utime},
361 // Percentage displays
362 {"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
363 {"%CPU", 4, SLOT_utime2},
366 {"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
367 {"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
368 {"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
369 {"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
372 {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
373 {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
376 // Return 0 to discard, nonzero to keep
377 static int shared_match_process(long long *slot)
379 struct ptr_len match[] = {
380 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
381 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
382 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
387 // Do we have -g -G -p -P -s -t -u -U options selecting processes?
388 for (i = 0; i < ARRAY_LEN(match); i++) {
389 struct ptr_len *mm = match[i].ptr;
393 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
401 // Return 0 to discard, nonzero to keep
402 static int ps_match_process(long long *slot)
404 int i = shared_match_process(slot);
407 // If we had selections and didn't match them, don't display
410 // Filter implicit categories for other display types
411 if ((toys.optflags&(FLAG_a|FLAG_d)) && slot[SLOT_sid]==*slot) return 0;
412 if ((toys.optflags&FLAG_a) && !slot[SLOT_ttynr]) return 0;
413 if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e))
414 && TT.tty!=slot[SLOT_ttynr]) return 0;
419 // Convert field to string representation
420 static char *string_field(struct carveup *tb, struct strawberry *field)
422 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
423 int which = field->which, sl = typos[which].slot;
424 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
426 // numbers, mostly from /proc/$PID/stat
427 if (which <= PS_BIT) {
430 if (which==PS_PRI) ll = 39-ll;
431 if (which==PS_ADDR) fmt = "%llx";
432 else if (which==PS_SZ) ll >>= 12;
433 else if (which==PS_RSS) ll <<= 2;
434 else if (which==PS_VSZ) ll >>= 10;
435 else if (which==PS_PR && ll<-9) fmt="RT";
436 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
437 sprintf(out, fmt, ll);
443 // First string slot has offset 0, others are offset[-slot-2]
444 if (--sl) out += tb->offset[--sl];
445 if (which==PS_ARGS || which==PS_COMM) {
449 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
450 if (out[i] == '/') s = out+i+1;
453 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
456 } else if (which <= PS_RGROUP) {
457 sprintf(out, "%lld", ll);
459 if (which > PS_RUSER) {
460 struct group *gr = bufgetgrgid(ll);
462 if (gr) out = gr->gr_name;
464 struct passwd *pw = bufgetpwuid(ll);
466 if (pw) out = pw->pw_name;
471 } else if (which <= PS_TIME_) {
472 int unit = 60, pad = 2, j = TT.ticks;
475 if (which!=PS_TIME_) unit *= 60*24;
477 // top adjusts slot[SLOT_upticks], we want original meaning.
478 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
481 // Output days-hours:mins:secs, skipping non-required fields with zero
482 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top
483 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
484 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
486 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
488 if ((*s = "-::"[j])) s++;
493 if (which==PS_TIME_ && s-out<8)
494 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
496 // Percentage displays
497 } else if (which <= PS__CPU) {
498 ll = slot[sl&63]*1000;
499 if (which==PS__VSZ || which==PS__MEM)
500 ll /= TT.si.totalram/((which==PS__VSZ) ? 1024 : 4096);
501 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
503 if (which==PS_C) sl += 5;
504 sprintf(out, "%d", sl/10);
505 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
508 } else if (which <= PS_DIO) {
509 ll = slot[typos[which].slot];
510 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
511 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
512 else human_readable(out, ll, 0);
514 // Posix doesn't specify what flags should say. Man page says
515 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
516 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
517 else if (which==PS_S || which==PS_STAT) {
520 if (which==PS_STAT) {
521 // TODO l = multithreaded
522 if (slot[SLOT_nice]<0) *s++ = '<';
523 else if (slot[SLOT_nice]>0) *s++ = 'N';
524 if (slot[SLOT_sid]==*slot) *s++ = 's';
525 if (slot[SLOT_vmlck]) *s++ = 'L';
526 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
529 } else if (which==PS_STIME) {
530 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
532 // Padding behavior's a bit odd: default field size is just hh:mm.
533 // Increasing stime:size reveals more data at left until full,
534 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16,
535 // then add :ss on right for :19.
536 strftime(out, 260, "%F %T", localtime(&t));
537 out = out+strlen(out)-3-abs(field->len);
538 if (out<buf) out = buf;
540 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
541 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
546 // Display process data that get_ps() read from /proc, formatting with TT.fields
547 static void show_ps(struct carveup *tb)
549 struct strawberry *field;
550 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
552 // Loop through fields to display
553 for (field = TT.fields; field; field = field->next) {
554 char *out = string_field(tb, field);
556 // Output the field, appropriately padded
558 // Minimum one space between each field
559 if (field != TT.fields) {
564 // Don't truncate number fields, but try to reclaim extra offset from later
565 // fields that can naturally be shorter
566 abslen = abs(field->len);
567 sign = field->len<0 ? -1 : 1;
569 if (field->which<=PS_BIT && olen>abslen) {
570 // overflow but remember by how much
571 extra += olen-abslen;
573 } else if (extra && olen<abslen) {
574 // If later fields have slack space, take back overflow
576 if (olen>extra) olen = extra;
580 if (abslen>width) abslen = width;
583 // If last field is left justified, no trailing spaces.
584 if (!field->next && sign<0) {
589 if (TT.tty) width -= draw_trim(out, pad, len);
590 else width -= printf("%*.*s", pad, len, out);
593 xputc(TT.time ? '\r' : '\n');
596 // dirtree callback: read data about process to display, store, or discard it.
597 // Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
598 // (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
599 static int get_ps(struct dirtree *new)
602 char *name; // Path under /proc/$PID directory
603 long long bits; // Only fetch extra data if an -o field is displaying it
605 // sources for carveup->offset[] data
606 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
607 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
610 struct carveup *tb = (void *)toybuf;
611 long long *slot = tb->slot;
612 char *name, *s, *buf = tb->str, *end = 0;
616 // Recurse one level into /proc children, skip non-numeric entries
618 return DIRTREE_RECURSE|DIRTREE_SHUTUP
619 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
621 memset(slot, 0, sizeof(tb->slot));
622 if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
623 if (TT.threadparent && TT.threadparent->extra)
624 if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
625 fd = dirtree_parentfd(new);
628 sprintf(buf, "%lld/stat", *slot);
629 if (!readfileat(fd, buf, buf, &len)) return 0;
631 // parse oddball fields (name and state). Name can have embedded ')' so match
632 // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
633 // All remaining fields should be numeric.
634 if (!(name = strchr(buf, '('))) return 0;
635 for (s = ++name; *s; s++) if (*s == ')') end = s;
636 if (!end || end-name>255) return 0;
638 // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
639 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
640 for (j = 1; j<SLOT_count; j++)
641 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
643 // Now we've read the data, move status and name right after slot[] array,
644 // and convert low chars to ? for non-tty display while we're at it.
645 for (i = 0; i<end-name; i++)
646 if ((tb->str[i] = name[i]) < ' ')
647 if (!TT.tty) tb->str[i] = '?';
650 len = sizeof(toybuf)-(buf-toybuf);
652 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
653 // or numeric wchan, and the remaining two are always zero), and vmlck into
654 // 18 (which is "obsolete, always 0" from stat)
655 slot[SLOT_uid] = new->st.st_uid;
656 slot[SLOT_gid] = new->st.st_gid;
658 // TIME and TIME+ use combined value, ksort needs 'em added.
659 slot[SLOT_utime] += slot[SLOT_stime];
660 slot[SLOT_utime2] = slot[SLOT_utime];
662 // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
663 // and save ruid, rgid, and vmlck.
664 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
665 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
669 sprintf(buf, "%lld/status", *slot);
670 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
671 s = strafter(buf, "\nUid:");
672 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
673 s = strafter(buf, "\nGid:");
674 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
675 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
676 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
679 // Do we need to read "io"?
680 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
683 sprintf(buf, "%lld/io", *slot);
684 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
685 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
686 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
687 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
688 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
689 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
690 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
693 // We now know enough to skip processes we don't care about.
694 if (TT.match_process && !TT.match_process(slot)) return 0;
696 // /proc data is generated as it's read, so for maximum accuracy on slow
697 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
699 slot[SLOT_uptime] = TT.si.uptime;
700 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
702 // Do we need to read "statm"?
703 if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
706 sprintf(buf, "%lld/statm", *slot);
707 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
709 for (s = buf, i=0; i<3; i++)
710 if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
714 // Do we need to read "exe"?
715 if (TT.bits&_PS_BIT) {
718 sprintf(buf, "%lld/exe", *slot);
719 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
720 if (buf[4] == 1) slot[SLOT_bits] = 32;
721 else if (buf[4] == 2) slot[SLOT_bits] = 64;
725 // Do we need Android scheduling policy?
726 if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
728 // Fetch string data while parentfd still available, appending to buf.
729 // (There's well over 3k of toybuf left. We could dynamically malloc, but
730 // it'd almost never get used, querying length of a proc file is awkward,
731 // fixed buffer is nommu friendly... Wait for somebody to complain. :)
732 slot[SLOT_argv0len] = 0;
733 for (j = 0; j<ARRAY_LEN(fetch); j++) {
734 tb->offset[j] = buf-(tb->str);
735 if (!(TT.bits&fetch[j].bits)) {
740 // Determine remaining space, reserving minimum of 256 bytes/field and
741 // 260 bytes scratch space at the end (for output conversion later).
742 len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
743 sprintf(buf, "%lld/%s", *slot, fetch[j].name);
745 // For exe we readlink instead of read contents
747 struct carveup *ptb = 0;
750 // Thread doesn't have exe or argv[0], so use parent's
751 if (TT.threadparent && TT.threadparent->extra)
752 ptb = (void *)TT.threadparent->extra;
754 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
756 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
758 if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
759 i = ptb->slot[SLOT_argv0len];
760 s = ptb->str+ptb->offset[4];
761 while (-1!=(k = stridx(s, '/')) && k<i) {
771 // If it's not the TTY field, data we want is in a file.
772 // Last length saved in slot[] is command line (which has embedded NULs)
774 int rdev = slot[SLOT_ttynr];
777 // Call no tty "?" rather than "0:0".
780 // Can we readlink() our way to a name?
781 for (i = 0; i<3; i++) {
782 sprintf(buf, "%lld/fd/%i", *slot, i);
783 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
784 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
788 // Couldn't find it, try all the tty drivers.
790 FILE *fp = fopen("/proc/tty/drivers", "r");
791 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
794 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
795 // TODO: we could parse the minor range too.
796 if (tty_major == maj) {
798 len += sprintf(buf+len, "%d", min);
799 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
807 // Really couldn't find it, so just show major:minor.
808 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
812 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
815 // Data we want is in a file.
816 // Last length saved in slot[] is command line (which has embedded NULs)
819 // When command has no arguments, don't space over the NUL
820 if (readfileat(fd, buf, buf, &len) && len>0) {
823 // Trim trailing whitespace and NUL bytes
825 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
828 // Turn NUL to space, other low ascii to ? (in non-tty mode)
829 // cmdline has a trailing NUL that we don't want to turn to space.
830 for (i=0; i<len-1; i++) {
836 } else if (!TT.tty && c<' ') c = '?';
839 // Store end of argv[0] so ARGS and CMDLINE can differ.
840 // We do it for each file string slot but last is cmdline, which sticks.
841 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
842 } else *buf = len = 0;
845 // Above calculated/retained len, so we don't need to re-strlen.
850 if (TT.show_process && !TT.threadparent) {
856 // If we need to sort the output, add it to the list and return.
857 s = xmalloc(buf-toybuf);
858 new->extra = (long)s;
859 memcpy(s, toybuf, buf-toybuf);
864 static int get_threads(struct dirtree *new)
868 unsigned pid, kcount;
870 if (!new->parent) return get_ps(new);
872 if (!(pid = atol(new->name))) return 0;
874 TT.threadparent = new;
881 // Recurse down into tasks, retaining thread groups.
882 // Disable show_process at least until we can calculate tcount
884 sprintf(toybuf, "/proc/%u/task", pid);
885 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
887 kcount = TT.kcount-kcount+1;
888 tb = (void *)new->extra;
889 tb->slot[SLOT_tcount] = kcount;
891 // Fill out tid and thread count for each entry in group
892 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
893 tb = (void *)dt->extra;
894 tb->slot[SLOT_pid] = pid;
895 tb->slot[SLOT_tcount] = kcount;
899 if (!TT.show_process) return DIRTREE_SAVE;
900 TT.show_process((void *)new->extra);
904 new = dt->child->next;
905 TT.show_process((void *)dt->child->extra);
914 static char *parse_ko(void *data, char *type, int length)
916 struct strawberry *field;
917 char *width, *title, *end, *s;
920 // Get title, length of title, type, end of type, and display width
922 // Chip off =name to display
923 if ((end = strchr(type, '=')) && length>(end-type)) {
925 length -= (end-type)+1;
931 // Chip off :width to display
932 if ((width = strchr(type, ':')) && width<end) {
933 if (!title) length = width-type;
936 // Allocate structure, copy title
937 field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
939 memcpy(field->title = field->forever, title, length);
940 field->title[field->len = length] = 0;
944 field->len = strtol(++width, &title, 10);
945 if (!isdigit(*width) || title != end) return title;
951 if (*type == '-') field->reverse = -1;
952 else if (*type != '+') type--;
954 for (i = 0; i<ARRAY_LEN(typos); i++) {
956 for (j = 0; j<2; j++) {
957 if (!j) s = typos[i].name;
958 // posix requires alternate names for some fields
959 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
960 PS_VSZ, PS_USER, 0}, i))) continue;
962 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
964 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
968 if (i==ARRAY_LEN(typos)) return type;
969 if (!field->title) field->title = typos[field->which].name;
970 if (!field->len) field->len = typos[field->which].width;
971 else if (typos[field->which].width<0) field->len *= -1;
972 dlist_add_nomalloc(data, (void *)field);
977 long long get_headers(struct strawberry *fields, char *buf, int blen)
982 for (; fields; fields = fields->next) {
983 len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
985 bits |= 1LL<<fields->which;
991 // Parse -p -s -t -u -U -g -G
992 static char *parse_rest(void *data, char *str, int len)
994 struct ptr_len *pl = (struct ptr_len *)data;
999 // Allocate next chunk of data
1001 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1003 // Parse numerical input
1004 if (isdigit(*str)) {
1005 ll[pl->len] = xstrtol(str, &end, 10);
1006 if (end==(len+str)) num++;
1007 // For pkill, -s 0 represents pkill's session id.
1008 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1011 if (pl==&TT.pp || pl==&TT.ss) {
1012 if (num && ll[pl->len]>0) {
1017 } else if (pl==&TT.tt) {
1018 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0
1020 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1021 if (strstart(&str, "pts/")) {
1024 } else if (strstart(&str, "tty")) len -= 3;
1026 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1029 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1030 memcpy(end, str, len);
1033 ll[pl->len++] = st.st_rdev;
1037 } else if (len<255) {
1046 memcpy(name, str, len);
1048 if (pl==&TT.gg || pl==&TT.GG) {
1049 struct group *gr = getgrnam(name);
1051 ll[pl->len++] = gr->gr_gid;
1055 } else if (pl==&TT.uu || pl==&TT.UU) {
1056 struct passwd *pw = getpwnam(name);
1058 ll[pl->len++] = pw->pw_uid;
1070 static int ksort(void *aa, void *bb)
1072 struct strawberry *field;
1073 struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
1076 for (field = TT.kfields; field && !ret; field = field->next) {
1077 slot = typos[field->which].slot;
1079 // Can we do numeric sort?
1081 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1082 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1085 // fallback to string sort
1087 memccpy(toybuf, string_field(ta, field), 0, 2048);
1089 ret = strcmp(toybuf, string_field(tb, field));
1091 ret *= field->reverse;
1097 static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
1100 struct dirtree *next = dt->next;
1102 if (dt->extra) *(tb++) = (void *)dt->extra;
1103 if (dt->child) tb = collate_leaves(tb, dt->child);
1111 static struct carveup **collate(int count, struct dirtree *dt)
1113 struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
1115 collate_leaves(tbsort, dt);
1120 static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1122 struct arg_list def;
1124 memset(&def, 0, sizeof(struct arg_list));
1126 comma_args(arg ? arg : &def, fields, err, parse_ko);
1129 static void shared_main(void)
1133 TT.ticks = sysconf(_SC_CLK_TCK);
1135 TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
1137 terminal_size(&TT.width, &TT.height);
1140 // find controlling tty, falling back to /dev/tty if none
1141 for (i = 0; !TT.tty && i<4; i++) {
1145 if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
1147 if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
1148 if (i==3) close(fd);
1158 if (toys.optflags&FLAG_w) TT.width = 99999;
1161 // parse command line options other than -o
1162 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1163 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1164 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1165 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1166 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1167 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1168 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1169 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1170 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1171 dlist_terminate(TT.kfields);
1173 // Figure out which fields to display
1174 not_o = "%sTTY,TIME,CMD";
1175 if (toys.optflags&FLAG_f)
1176 sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
1177 (toys.optflags&FLAG_T) ? "TCNT" : "C");
1178 else if (toys.optflags&FLAG_l)
1179 not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
1180 else if (CFG_TOYBOX_ON_ANDROID)
1181 not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
1182 sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
1184 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
1185 if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
1186 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1189 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
1190 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1191 if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
1193 dlist_terminate(TT.fields);
1195 // -f and -n change the meaning of some fields
1196 if (toys.optflags&(FLAG_f|FLAG_n)) {
1197 struct strawberry *ever;
1199 for (ever = TT.fields; ever; ever = ever->next) {
1200 if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
1201 && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
1206 // Calculate seen fields bit array, and if we aren't deferring printing
1207 // print headers now (for low memory/nommu systems).
1208 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1209 if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
1210 if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
1211 TT.match_process = ps_match_process;
1212 dt = dirtree_read("/proc",
1213 ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1214 ? get_threads : get_ps);
1216 if (toys.optflags&(FLAG_k|FLAG_M)) {
1217 struct carveup **tbsort = collate(TT.kcount, dt);
1219 if (toys.optflags&FLAG_M) {
1220 for (i = 0; i<TT.kcount; i++) {
1221 struct strawberry *field;
1223 for (field = TT.fields; field; field = field->next) {
1224 int len = strlen(string_field(tbsort[i], field));
1226 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1230 // Now that we've recalculated field widths, re-pad headers again
1231 get_headers(TT.fields, toybuf, sizeof(toybuf));
1232 printf("%.*s\n", TT.width, toybuf);
1235 if (toys.optflags&FLAG_k)
1236 qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
1237 for (i = 0; i<TT.kcount; i++) {
1241 if (CFG_TOYBOX_FREE) free(tbsort);
1244 if (CFG_TOYBOX_FREE) {
1253 llist_traverse(TT.fields, free);
1259 #include "generated/flags.h"
1261 // select which of the -o fields to sort by
1262 static void setsort(int pos)
1264 struct strawberry *field, *going2;
1269 for (field = TT.fields; field; field = field->next) {
1270 if ((TT.sortpos = i++)<pos && field->next) continue;
1271 going2 = TT.kfields;
1272 going2->which = field->which;
1273 going2->len = field->len;
1278 // If we have both, adjust slot[deltas[]] to be relative to previous
1279 // measurement rather than process start. Stomping old.data is fine
1280 // because we free it after displaying.
1281 static int merge_deltas(long long *oslot, long long *nslot, int milis)
1283 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1284 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1287 for (i = 0; i<ARRAY_LEN(deltas); i++)
1288 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1289 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1294 static int header_line(int line, int rev)
1296 if (!line) return 0;
1298 if (toys.optflags&FLAG_b) rev = 0;
1300 printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
1301 (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
1302 rev ? "\033[0m" : "");
1307 static long long millitime(void)
1311 clock_gettime(CLOCK_MONOTONIC, &ts);
1312 return ts.tv_sec*1000+ts.tv_nsec/1000000;
1315 static void top_common(
1316 int (*filter)(long long *oslot, long long *nslot, int milis))
1318 long long timeout = 0, now, stats[16];
1320 struct carveup **tb;
1323 } plist[2], *plold, *plnew, old, new, mix;
1324 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1325 "iow", "irq", "sirq", "host"};
1328 int i, lines, topoff = 0, done = 0;
1330 toys.signal = SIGWINCH;
1331 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1333 memset(plist, 0, sizeof(plist));
1334 memset(stats, 0, sizeof(stats));
1339 plold = plist+(tock++&1);
1340 plnew = plist+(tock&1);
1341 plnew->whence = millitime();
1342 dt = dirtree_read("/proc",
1343 ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
1344 ? get_threads : get_ps);
1345 plnew->tb = collate(plnew->count = TT.kcount, dt);
1348 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1349 long long *st = stats+8*(tock&1);
1351 // user nice system idle iowait irq softirq host
1352 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1353 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1356 // First time, wait a quarter of a second to collect a little delta data.
1362 // Collate old and new into "mix", depends on /proc read in pid sort order
1365 mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
1368 while (old.count || new.count) {
1369 struct carveup *otb = *old.tb, *ntb = *new.tb;
1371 // If we just have old for this process, it exited. Discard it.
1372 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1379 // If we just have new, use it verbatim
1380 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1383 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1384 mix.tb[mix.count] = otb;
1394 // Don't re-fetch data if it's not time yet, just re-display existing data.
1399 qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
1400 if (!(toys.optflags&FLAG_b)) {
1401 printf("\033[H\033[J");
1404 terminal_probesize(&TT.width, &TT.height);
1409 if (recalc && !(toys.optflags&FLAG_q)) {
1410 // Display "top" header.
1411 if (*toys.which->name == 't') {
1412 struct strawberry alluc;
1413 long long ll, up = 0;
1417 // Count running, sleeping, stopped, zombie processes.
1419 memset(run, 0, sizeof(run));
1420 for (i = 0; i<mix.count; i++)
1421 run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
1423 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1424 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1425 lines = header_line(lines, 0);
1427 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1428 for (i=0; i<6; i++) {
1429 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1430 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1431 run[i] = pos ? atol(pos) : 0;
1434 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1435 run[0], run[0]-run[1], run[1], run[2]);
1436 lines = header_line(lines, 0);
1438 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1439 run[4], run[4]-run[5], run[5], run[3]);
1440 lines = header_line(lines, 0);
1444 i = sysconf(_SC_NPROCESSORS_CONF);
1445 pos += sprintf(pos, "%d%%cpu", i*100);
1448 // If a processor goes idle it's powered down and its idle ticks don't
1449 // advance, so calculate idle time as potential time - used.
1450 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1453 ll = stats[3] = stats[11] = 0;
1454 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1455 stats[3] = now - llabs(ll);
1457 for (i = 0; i<8; i++) {
1458 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1459 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1461 lines = header_line(lines, 0);
1463 struct strawberry *fields;
1466 memset(&tb, 0, sizeof(struct carveup));
1467 pos = stpcpy(toybuf, "Totals:");
1468 for (fields = TT.fields; fields; fields = fields->next) {
1469 long long ll, bits = 0;
1470 int slot = typos[fields->which].slot&63;
1472 if (fields->which<PS_C || fields->which>PS_DIO) continue;
1473 ll = 1LL<<fields->which;
1474 if (bits&ll) continue;
1476 for (i=0; i<mix.count; i++)
1477 tb.slot[slot] += mix.tb[i]->slot[slot];
1478 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1479 " %s: %*s,", typos[fields->which].name,
1480 fields->len, string_field(&tb, fields));
1483 lines = header_line(lines, 0);
1486 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1487 for (i = 0, is = ' '; *pos; pos++) {
1490 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1492 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1495 lines = header_line(lines, 1);
1497 if (!recalc && !(toys.optflags&FLAG_b))
1498 printf("\033[%dH\033[J", 1+TT.height-lines);
1501 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1502 if (!(toys.optflags&FLAG_b) && i) xputc('\n');
1503 show_ps(mix.tb[i+topoff]);
1506 if (TT.top.n && !--TT.top.n) {
1512 if (timeout<=now) timeout = new.whence+TT.top.d;
1513 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1515 // In batch mode, we ignore the keyboard.
1516 if (toys.optflags&FLAG_b) {
1517 msleep(timeout-now);
1518 // Make an obvious gap between datasets.
1523 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1524 if (i==-1 || i==3 || toupper(i)=='Q') {
1530 // Flush unknown escape sequences.
1531 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1535 } else if (toupper(i)=='R')
1536 ((struct strawberry *)TT.kfields)->reverse *= -1;
1539 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1540 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1541 // KEY_UP is 0, so at end of strchr
1542 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1545 if (i == KEY_UP) topoff--;
1546 else if (i == KEY_DOWN) topoff++;
1547 else if (i == KEY_PGDN) topoff += lines;
1548 else if (i == KEY_PGUP) topoff -= lines;
1549 if (topoff<0) topoff = 0;
1550 if (topoff>mix.count) topoff = mix.count;
1557 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1561 if (!(toys.optflags&FLAG_b)) tty_reset();
1564 static void top_setup(char *defo, char *defk)
1567 if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
1569 TT.time = millitime();
1570 set_terminal(0, 1, 0);
1571 sigatexit(tty_sigreset);
1572 xsignal(SIGWINCH, generic_signal);
1573 printf("\033[?25l\033[0m");
1577 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1578 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1579 TT.match_process = shared_match_process;
1581 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1582 dlist_terminate(TT.fields);
1584 // First (dummy) sort field is overwritten by setsort()
1585 default_ko("-S", &TT.kfields, 0, 0);
1586 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1587 dlist_terminate(TT.kfields);
1588 setsort(TT.top.s-1);
1593 // usage: [-h HEADER] -o OUTPUT -k SORT
1595 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
1596 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
1597 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1598 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1600 struct strawberry *fields = TT.fields;
1602 fields = fields->next->next;
1603 comma_args(TT.top.O, &fields, "bad -O", parse_ko);
1606 top_common(merge_deltas);
1611 #include "generated/flags.h"
1613 static int iotop_filter(long long *oslot, long long *nslot, int milis)
1615 if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
1616 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1618 return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
1621 void iotop_main(void)
1623 char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
1625 if (toys.optflags&FLAG_K) TT.forcek++;
1627 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1628 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1631 top_common(iotop_filter);
1634 // pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
1635 // context, so force pgrep's flags on even when building pkill standalone.
1636 // (All the pgrep/pkill functions drop out when building ps standalone.)
1638 #define CLEANUP_iotop
1640 #include "generated/flags.h"
1643 struct regex_list *next;
1647 static void do_pgk(struct carveup *tb)
1649 if (TT.pgrep.signal) {
1650 if (kill(*tb->slot, TT.pgrep.signal)) {
1651 char *s = num_to_sig(TT.pgrep.signal);
1653 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1654 perror_msg("%s->%lld", s, *tb->slot);
1657 if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
1658 printf("%lld", *tb->slot);
1659 if (toys.optflags&FLAG_l)
1660 printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
1662 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1666 static void match_pgrep(struct carveup *tb)
1669 struct regex_list *reg;
1670 char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
1672 // Never match ourselves.
1673 if (TT.pgrep.self == *tb->slot) return;
1675 if (TT.pgrep.regexes) {
1676 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1677 if (regexec(®->reg, name, 1, &match, 0)) continue;
1678 if (toys.optflags&FLAG_x)
1679 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1682 if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
1685 // pgrep should return success if there's a match.
1688 // Repurpose a field for -c count.
1690 if (toys.optflags&(FLAG_n|FLAG_o)) {
1691 long long ll = tb->slot[SLOT_starttime];
1693 if (toys.optflags&FLAG_o) ll *= -1;
1694 if (TT.time && TT.time>ll) return;
1696 free(TT.pgrep.snapshot);
1697 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1701 static int pgrep_match_process(long long *slot)
1703 int match = shared_match_process(slot);
1705 return (toys.optflags&FLAG_v) ? !match : match;
1708 void pgrep_main(void)
1711 struct regex_list *reg;
1713 TT.pgrep.self = getpid();
1715 // No signal names start with "L", so no need for "L: " parsing.
1716 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1717 error_exit("bad -L '%s'", TT.pgrep.L);
1719 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1720 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1721 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1722 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1723 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1724 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1725 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1727 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1728 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1729 if (!toys.optc) help_exit("No PATTERN");
1731 if (toys.optflags&FLAG_f) TT.bits |= _PS_CMDLINE;
1732 for (arg = toys.optargs; *arg; arg++) {
1733 reg = xmalloc(sizeof(struct regex_list));
1734 xregcomp(®->reg, *arg, REG_EXTENDED);
1735 reg->next = TT.pgrep.regexes;
1736 TT.pgrep.regexes = reg;
1738 TT.match_process = pgrep_match_process;
1739 TT.show_process = (void *)match_pgrep;
1741 // pgrep should return failure if there are no matches.
1744 dirtree_read("/proc", get_ps);
1745 if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
1746 if (TT.pgrep.snapshot) {
1747 do_pgk(TT.pgrep.snapshot);
1748 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1750 if (TT.pgrep.d) xputc('\n');
1753 #define CLEANUP_pgrep
1755 #include "generated/flags.h"
1757 void pkill_main(void)
1759 char **args = toys.optargs;
1761 if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1762 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1763 if (toys.optflags & FLAG_V) TT.tty = 1;