1/* ps.c - show process list 2 * 3 * Copyright 2015 Rob Landley <rob@landley.net> 4 * 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() 8 * 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. 18 * 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. 27 * 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]). 32 * 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.) 38 * 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. 46 47USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE)) 48// stayroot because iotop needs root to read other process' proc/$$/io 49// TOP and IOTOP have a large common option block used for common processing, 50// the default values are different but the flags are in the same order. 51USE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d%<100=3000m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) 52USE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d%<100=3000m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE)) 53USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) 54USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) 55 56config PS 57 bool "ps" 58 default y 59 help 60 usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,] 61 62 List processes. 63 64 Which processes to show (-gGuUpPt selections may be comma separated lists): 65 66 -A All -a Has terminal not session leader 67 -d All but session leaders -e Synonym for -A 68 -g In GROUPs -G In real GROUPs (before sgid) 69 -p PIDs (--pid) -P Parent PIDs (--ppid) 70 -s In session IDs -t Attached to selected TTYs 71 -T Show threads also -u Owned by selected USERs 72 -U Real USERs (before suid) 73 74 Output modifiers: 75 76 -k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths 77 -n Show numeric USER and GROUP -w Wide output (don't truncate fields) 78 79 Which FIELDs to show. (-o HELP for list, default = -o PID,TTY,TIME,CMD) 80 81 -f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD) 82 -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD) 83 -o Output FIELDs instead of defaults, each with optional :size and =title 84 -O Add FIELDS to defaults 85 -Z Include LABEL 86 87config TOP 88 bool "top" 89 default y 90 help 91 usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,] 92 93 Show process activity in real time. 94 95 -H Show threads 96 -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID) 97 -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE) 98 -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default) 99 -s Sort by field number (1-X, default 9) 100 -b Batch mode (no tty) 101 -d Delay SECONDS between each cycle (default 3) 102 -m Maximum number of tasks to show 103 -n Exit after NUMBER iterations 104 -p Show these PIDs 105 -u Show these USERs 106 -q Quiet (no header lines) 107 108 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force 109 update, R to reverse sort, Q to exit. 110 111# Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io 112config IOTOP 113 bool "iotop" 114 default y 115 help 116 usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] 117 118 Rank processes by I/O. 119 120 -A All I/O, not just disk 121 -a Accumulated I/O (not percentage) 122 -H Show threads 123 -K Kilobytes 124 -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID) 125 -m Maximum number of tasks to show 126 -O Only show processes doing I/O 127 -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM) 128 -s Sort by field number (0-X, default 6) 129 -b Batch mode (no tty) 130 -d Delay SECONDS between each cycle (default 3) 131 -n Exit after NUMBER iterations 132 -p Show these PIDs 133 -u Show these USERs 134 -q Quiet (no header lines) 135 136 Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force 137 update, R to reverse sort, Q to exit. 138 139config PGREP 140 bool "pgrep" 141 default y 142 help 143 usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] 144 145 Search for process(es). PATTERN is an extended regular expression checked 146 against command names. 147 148 -c Show only count of matches 149 -d Use DELIM instead of newline 150 -L Send SIGNAL instead of printing name 151 -l Show command name 152 -f Check full command line for PATTERN 153 -G Match real Group ID(s) 154 -g Match Process Group(s) (0 is current user) 155 -n Newest match only 156 -o Oldest match only 157 -P Match Parent Process ID(s) 158 -s Match Session ID(s) (0 for current) 159 -t Match Terminal(s) 160 -U Match real User ID(s) 161 -u Match effective User ID(s) 162 -v Negate the match 163 -x Match whole command (not substring) 164 165config PKILL 166 bool "pkill" 167 default y 168 help 169 usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] 170 171 -l Send SIGNAL (default SIGTERM) 172 -V Verbose 173 -f Check full command line for PATTERN 174 -G Match real Group ID(s) 175 -g Match Process Group(s) (0 is current user) 176 -n Newest match only 177 -o Oldest match only 178 -P Match Parent Process ID(s) 179 -s Match Session ID(s) (0 for current) 180 -t Match Terminal(s) 181 -U Match real User ID(s) 182 -u Match effective User ID(s) 183 -v Negate the match 184 -x Match whole command (not substring) 185*/ 186 187#define FOR_ps 188#include "toys.h" 189 190GLOBALS( 191 union { 192 struct { 193 struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k; 194 } ps; 195 struct { 196 long n, m, d, s; 197 struct arg_list *u, *p, *o, *k, *O; 198 } top; 199 struct { 200 char *L; 201 struct arg_list *G, *g, *P, *s, *t, *U, *u; 202 char *d; 203 204 void *regexes, *snapshot; 205 int signal; 206 pid_t self, match; 207 } pgrep; 208 }; 209 210 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU; 211 struct dirtree *threadparent; 212 unsigned width, height; 213 dev_t tty; 214 void *fields, *kfields; 215 long long ticks, bits, time; 216 int kcount, forcek, sortpos; 217 int (*match_process)(long long *slot); 218 void (*show_process)(void *tb); 219) 220 221// Linked list of -o fields selected for display, in order, with :len and =title 222 223struct ofields { 224 struct ofields *next, *prev; 225 short which, len, reverse; 226 char *title; 227}; 228 229/* The function get_ps() reads all the data about one process, saving it in 230 * toybox as a struct procpid. Simple ps calls then pass toybuf directly to 231 * show_ps(), but features like sorting append a copy to a linked list 232 * for further processing once all processes have been read. 233 * 234 * struct procpid contains a slot[] array of 64 bit values, with the following 235 * data at each position in the array. Most is read from /proc/$PID/stat (see 236 * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but 237 * we replace several fields with don't use with other data. */ 238 239enum { 240 SLOT_pid, /*process id*/ SLOT_ppid, // parent process id 241 SLOT_pgrp, /*process group*/ SLOT_sid, // session id 242 SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty 243 SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults 244 SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults 245 SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies 246 SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime 247 SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level 248 SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count 249 SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot 250 SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size 251 SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr 252 SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address 253 SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer 254 SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes 255 SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id 256 SLOT_ruid, /*real user id*/ SLOT_gid, // group id 257 SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent 258 SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority 259 SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time 260 SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child 261 SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss 262// end of /proc/$PID/stat fields 263 SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length 264 SLOT_uptime, /*sysinfo.uptime*/ SLOT_totalram, // sysinfo.totalram 265 SLOT_vsz, /*Virtual mem Size*/ SLOT_shr, // Shared memory 266 SLOT_pcy, /*Android sched pol*/ SLOT_rchar, // All bytes read 267 SLOT_wchar, /*All bytes written*/ SLOT_rbytes, // Disk bytes read 268 SLOT_wbytes, /*Disk bytes written*/ SLOT_swap, // Swap pages used 269 SLOT_bits, /*32 or 64*/ SLOT_tid, // Thread ID 270 SLOT_tcount, /*Thread count*/ 271 272 SLOT_count /* Size of array */ 273}; 274 275/* In addition to slot[], carevup contains 6 string fields to display 276 command name, tty device, selinux label... They're stored one after the 277 other in str[] (separated by null terminators), and offset[] contains the 278 starting position of each string after the first (which is always 0). */ 279 280// Data layout in toybuf 281struct procpid { 282 long long slot[SLOT_count]; // data (see enum above) 283 unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0) 284 char state; 285 char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME 286}; 287 288/* The typos[] array lists all the types understood by "ps -o", I.E all the 289 * columns ps and top know how to display. Each entry has: 290 * 291 * name: the column name, displayed at top and used to select column with -o 292 * 293 * width: the display width. Fields are padded to this width when displaying 294 * to a terminal (negative means right justified). Strings are truncated 295 * to fit, numerical fields are padded but not truncated (although 296 * the display code reclaims unused padding from later fields to try to 297 * get the overflow back). 298 * 299 * slot: which slot[] out of procpid. Negative means it's a string field. 300 * value|XX requests extra display/sort processing. 301 * 302 * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the 303 * first string argument and the prefix is the first argument to TAGGED_ARRAY 304 * so in this case "NAME" becomes PS_NAME which is the offset into typos[] 305 * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME). 306 * We record active columns in TT.bits, ala: 307 * 308 * if (TT.bits & _PS_NAME) printf("-o included PS_NAME"); 309 */ 310 311#define XX 64 // force string representation for sorting, etc 312 313// TODO: Android uses -30 for LABEL, but ideally it would auto-size. 314struct typography { 315 char *name, *help; 316 signed char width, slot; 317} static const typos[] = TAGGED_ARRAY(PS, 318 // Numbers. (What's in slot[] is what's displayed, sorted numerically.) 319 {"PID", "Process ID", 5, SLOT_pid}, 320 {"PPID", "Parent Process ID", 5, SLOT_ppid}, 321 {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority}, 322 {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice}, 323 {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip}, 324 {"SZ", "4k pages to swap out", 5, SLOT_vsize}, 325 {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss}, 326 {"PGID", "Process Group ID", 5, SLOT_pgrp}, 327 {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize}, 328 {"MAJFL", "Major page faults", 6, SLOT_majflt}, 329 {"MINFL", "Minor page faults", 6, SLOT_minflt}, 330 {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority}, 331 {"PSR", "Processor last executed on", 3, SLOT_taskcpu}, 332 {"RTPRIO", "Realtime priority", 6, SLOT_rtprio}, 333 {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)", 334 3, SLOT_policy}, 335 {"CPU", "Which processor running on", 3, SLOT_taskcpu}, 336 {"TID", "Thread ID", 5, SLOT_tid}, 337 {"TCNT", "Thread count", 4, SLOT_tcount}, 338 {"BIT", "32 or 64", 3, SLOT_bits}, 339 340 // String fields (-1 is procpid->str, rest are str+offset[1-slot]) 341 {"TTY", "Controlling terminal", -8, -2}, 342 {"WCHAN", "Wait location in kernel", -6, -3}, 343 {"LABEL", "Security label", -30, -4}, 344 {"COMM", "EXE filename (/proc/PID/exe)", -27, -5}, 345 {"NAME", "Process name (PID's argv[0])", -27, -7}, 346 {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5}, 347 {"CMDLINE", "Command line (argv[])", -27, -6}, 348 {"ARGS", "CMDLINE minus initial path", -27, -6}, 349 {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1}, 350 351 // user/group (may call getpwuid() or similar) 352 {"UID", "User id", 5, SLOT_uid}, 353 {"USER", "User name", -12, XX|SLOT_uid}, 354 {"RUID", "Real (before suid) user ID", 4, SLOT_ruid}, 355 {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid}, 356 {"GID", "Group ID", 8, SLOT_gid}, 357 {"GROUP", "Group name", -8, XX|SLOT_gid}, 358 {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid}, 359 {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid}, 360 361 // clock displays (00:00:00) 362 {"TIME", "CPU time consumed", 8, SLOT_utime}, 363 {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime}, 364 {"TIME+", "CPU time (high precision)", 9, SLOT_utime}, 365 366 // Percentage displays (fixed point, one decimal digit. 123 -> 12.3) 367 {"C", "Total %CPU used since start", 1, SLOT_utime2}, 368 {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize}, 369 {"%MEM", "RSS as % of physical memory", 5, SLOT_rss}, 370 {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2}, 371 372 // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc) 373 {"VIRT", "Virtual memory size", 4, SLOT_vsz}, 374 {"RES", "Short RSS", 4, SLOT_rss}, 375 {"SHR", "Shared memory", 4, SLOT_shr}, 376 {"READ", "Data read", 6, SLOT_rchar}, 377 {"WRITE", "Data written", 6, SLOT_wchar}, 378 {"IO", "Data I/O", 6, SLOT_iobytes}, 379 {"DREAD", "Data read from disk", 6, SLOT_rbytes}, 380 {"DWRITE", "Data written to disk", 6, SLOT_wbytes}, 381 {"SWAP", "Swap I/O", 6, SLOT_swap}, 382 {"DIO", "Disk I/O", 6, SLOT_diobytes}, 383 384 // Misc (special cases) 385 {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime}, 386 {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags}, 387 {"S", "Process state:\n" 388 "\t R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)\n" 389 "\t X (dead) Z (zombie) P (parked) I (idle)\n" 390 "\t Also between Linux 2.6.33 and 3.13:\n" 391 "\t x (dead) K (wakekill) W (waking)\n", 392 -1, XX}, 393 {"STAT", "Process state (S) plus:\n" 394 "\t < high priority N low priority L locked memory\n" 395 "\t s session leader + foreground l multithreaded", 396 -5, XX}, 397 {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy}, 398); 399 400// Show sorted "-o help" text for fields listed in toybuf[len] 401static void help_fields(int len, int multi) 402{ 403 int i, j, k, left = 0; 404 struct typography *t; 405 406 // Quick and dirty sort of toybuf[] entries (see TODO below) 407 for (j = len; j--; ) { 408 k = -1; 409 410 for (i=0; i<j; i++) { 411 if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) { 412 k = toybuf[i]; 413 toybuf[i] = toybuf[i+1]; 414 toybuf[i+1] = k; 415 } 416 } 417 if (k == -1) break; 418 } 419 420 // Display loop 421 for (i = j = 0; i<len; i++, j++) { 422 t = (void *)(typos+toybuf[i]); 423 if (strlen(t->help)>30) { 424 if (multi) printf(" %-8s%s\n", t->name, t->help); 425 else j--; 426 } else if (!multi) { 427 left = !(j&1); 428 printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left); 429 } 430 } 431 if (!multi && left) xputc('\n'); 432} 433 434// Print help text for each -o field, with categories. 435static void help_help(void) 436{ 437 int i, jump = PS_CMD+1-PS_COMM; 438 439 // TODO: sort the array of -o types so they're already alphabetical and 440 // don't need sorting here. A regex to find everything that currently cares 441 // about symbol order might be: "which *[><]=* *PS" 442 443 // First show the half-dozen variants of command line display. 444 445 printf("Command line field types:\n\n"); 446 for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i; 447 help_fields(jump, 0); 448 449 // Show the rest of the -o types, starting with the ones that don't columnize 450 451 printf("\nProcess attribute field types:\n\n"); 452 for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump; 453 help_fields(ARRAY_LEN(typos)-jump, 1); 454 help_fields(ARRAY_LEN(typos)-jump, 0); 455 456 xexit(); 457} 458 459// process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep 460static int shared_match_process(long long *slot) 461{ 462 struct ptr_len match[] = { 463 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid}, 464 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr}, 465 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid} 466 }; 467 int i, j; 468 long *ll = 0; 469 470 // Do we have -g -G -p -P -s -t -u -U options selecting processes? 471 for (i = 0; i < ARRAY_LEN(match); i++) { 472 struct ptr_len *mm = match[i].ptr; 473 474 if (mm->len) { 475 ll = mm->ptr; 476 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1; 477 } 478 } 479 480 return ll ? 0 : -1; 481} 482 483// process match filter for ps: Return 0 to discard, nonzero to keep 484static int ps_match_process(long long *slot) 485{ 486 int i = shared_match_process(slot); 487 488 if (i>0) return 1; 489 // If we had selections and didn't match them, don't display 490 if (!i) return 0; 491 492 // Filter implicit categories for other display types 493 if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0; 494 if (FLAG(a) && !slot[SLOT_ttynr]) return 0; 495 if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr]) 496 return 0; 497 498 return 1; 499} 500 501// Generate display string (260 bytes at end of toybuf) from struct ofield 502static char *string_field(struct procpid *tb, struct ofields *field) 503{ 504 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s; 505 int which = field->which, sl = typos[which].slot; 506 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0; 507 508 // numbers, mostly from /proc/$PID/stat 509 if (which <= PS_BIT) { 510 char *fmt = "%lld"; 511 512 if (which==PS_PRI) ll = 39-ll; 513 if (which==PS_ADDR) fmt = "%llx"; 514 else if (which==PS_SZ) ll >>= 12; 515 else if (which==PS_RSS) ll <<= 2; 516 else if (which==PS_VSZ) ll >>= 10; 517 else if (which==PS_PR && ll<-9) fmt="RT"; 518 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-"; 519 sprintf(out, fmt, ll); 520 521 // String fields 522 } else if (sl < 0) { 523 out = tb->str; 524 sl *= -1; 525 // First string slot has offset 0, others are offset[-slot-2] 526 if (--sl) out += tb->offset[--sl]; 527 if (which==PS_ARGS || which==PS_COMM) { 528 int i; 529 530 s = out; 531 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++) 532 if (out[i] == '/') s = out+i+1; 533 out = s; 534 } 535 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str); 536 537 // user/group 538 } else if (which <= PS_RGROUP) { 539 sprintf(out, "%lld", ll); 540 if (sl&XX) { 541 if (which > PS_RUSER) { 542 struct group *gr = bufgetgrgid(ll); 543 544 if (gr) out = gr->gr_name; 545 } else { 546 struct passwd *pw = bufgetpwuid(ll); 547 548 if (pw) out = pw->pw_name; 549 } 550 } 551 552 // Clock displays 553 } else if (which <= PS_TIME_) { 554 int unit = 60, pad = 2, j = TT.ticks; 555 time_t seconds; 556 557 if (which!=PS_TIME_) unit *= 60*24; 558 else pad = 0; 559 // top adjusts slot[SLOT_upticks], we want original meaning. 560 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime]; 561 seconds = ll/j; 562 563 // Output days-hours:mins:secs, skipping non-required fields with zero 564 // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top 565 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) { 566 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out; 567 if (s) { 568 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit)); 569 pad = 2; 570 if ((*s = "-::"[j])) s++; 571 } 572 seconds %= unit; 573 unit /= j ? 60 : 24; 574 } 575 if (which==PS_TIME_ && s-out<8) 576 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks); 577 578 // Percentage displays 579 } else if (which <= PS__CPU) { 580 ll = slot[sl&(XX-1)]*1000; 581 if (which==PS__VSZ || which==PS__MEM) 582 ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096); 583 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks]; 584 sl = ll; 585 if (which==PS_C) sl += 5; 586 sprintf(out, "%d", sl/10); 587 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10); 588 589 // Human readable 590 } else if (which <= PS_DIO) { 591 int i = abs(field->len); 592 593 if (i<4) i = 4; 594 s = out; 595 if ((ll = slot[typos[which].slot])<0) { 596 ll = -ll; 597 *s++ = '-'; 598 if (i>4) i--; 599 } 600 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE); 601 if (TT.forcek) sprintf(out, "%lldk", ll/1024); 602 else human_readable_long(s, ll, i-1, 0); 603 604 // Posix doesn't specify what flags should say. Man page says 605 // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h 606 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5); 607 else if (which==PS_S || which==PS_STAT) { 608 s = out; 609 *s++ = tb->state; 610 if (which==PS_STAT) { 611 // TODO l = multithreaded 612 if (slot[SLOT_nice]<0) *s++ = '<'; 613 else if (slot[SLOT_nice]>0) *s++ = 'N'; 614 if (slot[SLOT_sid]==*slot) *s++ = 's'; 615 if (slot[SLOT_vmlck]) *s++ = 'L'; 616 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+'; 617 } 618 *s = 0; 619 } else if (which==PS_STIME) { 620 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks; 621 622 // Padding behavior's a bit odd: default field size is just hh:mm. 623 // Increasing stime:size reveals more data at left until full, 624 // so move start address so yyyy-mm-dd hh:mm revealed on left at :16, 625 // then add :ss on right for :19. 626 strftime(out, 260, "%F %T", localtime(&t)); 627 out = out+strlen(out)-3-abs(field->len); 628 if (out<buf) out = buf; 629 630 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll)); 631 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which); 632 633 return out; 634} 635 636// Display process data that get_ps() read from /proc, formatting via TT.fields 637static void show_ps(void *p) 638{ 639 struct procpid *tb = p; 640 struct ofields *field; 641 int pad, len, width = TT.width, abslen, sign, olen, extra = 0; 642 643 // Loop through fields to display 644 for (field = TT.fields; field; field = field->next) { 645 char *out = string_field(tb, field); 646 647 // Output the field, appropriately padded 648 649 // Minimum one space between each field 650 if (width<2) break; 651 if (field != TT.fields) { 652 putchar(' '); 653 width--; 654 } 655 656 // Don't truncate number fields, but try to reclaim extra offset from later 657 // fields that can naturally be shorter 658 abslen = abs(field->len); 659 sign = field->len<0 ? -1 : 1; 660 olen = (TT.tty) ? utf8len(out) : strlen(out); 661 if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) { 662 // overflow but remember by how much 663 extra += olen-abslen; 664 abslen = olen; 665 } else if (extra && olen<abslen) { 666 int unused = abslen-olen; 667 668 // If later fields have slack space, take back overflow 669 if (unused>extra) unused = extra; 670 abslen -= unused; 671 extra -= unused; 672 } 673 if (abslen>width) abslen = width; 674 len = pad = abslen; 675 pad *= sign; 676 677 // If last field is left justified, no trailing spaces. 678 if (!field->next && sign<0) { 679 pad = -1; 680 len = width; 681 } 682 683 // If we truncated a left-justified field, show + instead of last char 684 if (olen>len && len>1 && sign<0) { 685 width--; 686 len--; 687 if (field->next) pad++; 688 abslen = 0; 689 } 690 691 if (TT.tty) width -= draw_trim(out, pad, len); 692 else width -= printf("%*.*s", pad, len, out); 693 if (!abslen) putchar('+'); 694 if (!width) break; 695 } 696 putchar(TT.time ? '\r' : '\n'); 697} 698 699// dirtree callback: read data about a process, then display or store it. 700// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra 701// (in -k mode) or calls show_ps directly on toybuf (for low memory systems). 702static int get_ps(struct dirtree *new) 703{ 704 struct { 705 char *name; // Path under /proc/$PID directory 706 long long bits; // Only fetch extra data if an -o field is displaying it 707 } fetch[] = { 708 // sources for procpid->offset[] data 709 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL}, 710 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}, 711 {"", _PS_NAME} 712 }; 713 struct procpid *tb = (void *)toybuf; 714 long long *slot = tb->slot; 715 char *name, *s, *buf = tb->str, *end = 0; 716 struct sysinfo si; 717 int i, j, fd; 718 off_t len; 719 720 // Recurse one level into /proc children, skip non-numeric entries 721 if (!new->parent) 722 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC 723 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process)); 724 725 // Grab PID and figure out if we're a thread or a process 726 memset(slot, 0, sizeof(tb->slot)); 727 slot[SLOT_tid] = *slot = atol(new->name); 728 if (TT.threadparent && TT.threadparent->extra) { 729 struct procpid *tb2 = (struct procpid *)TT.threadparent->extra; 730 731 *slot = *tb2->slot; 732 // Parent also shows up as a thread, but we need to reread task/stat fields 733 // to get non-collated info for just parent thread (vs whole process). 734 if (*slot == slot[SLOT_tid]) slot = tb2->slot; 735 } 736 fd = dirtree_parentfd(new); 737 738 // Read /proc/$PID/stat into half of toybuf. 739 len = 2048; 740 sprintf(buf, "%lld/stat", slot[SLOT_tid]); 741 if (!readfileat(fd, buf, buf, &len)) return 0; 742 743 // parse oddball fields: the first field is same as new->name (skip it) 744 // and the second and third (name and state) are the only non-numeric fields. 745 // Name has (parentheses) around it, and can have embedded ')' so match 746 // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that). 747 // TODO: kernel task struct actually limits name to 16 chars? 748 if (!(name = strchr(buf, '('))) return 0; 749 for (s = ++name; *s; s++) if (*s == ')') end = s; 750 if (!end || end-name>255) return 0; 751 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0; 752 753 // All remaining fields should be numeric, parse them into slot[] array 754 // (skipping first 3 stat fields and first slot[], both were handled above) 755 // yes this means the alignment's off: stat[4] becomes slot[1] 756 for (j = SLOT_ppid; j<SLOT_upticks; j++) 757 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break; 758 759 // Now we've read the data, move status and name right after slot[] array, 760 // and convert low chars to ? for non-tty display while we're at it. 761 for (i = 0; i<end-name; i++) 762 if ((tb->str[i] = name[i]) < ' ') 763 if (!TT.tty) tb->str[i] = '?'; 764 buf = tb->str+i; 765 *buf++ = 0; 766 len = sizeof(toybuf)-(buf-toybuf); 767 768 // Overwrite useless/obsolete stat fields with more interesting data. 769 770 // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch 771 // or numeric wchan, and the remaining two are always zero), and vmlck into 772 // 18 (which is "obsolete, always 0" from stat) 773 slot[SLOT_uid] = new->st.st_uid; 774 slot[SLOT_gid] = new->st.st_gid; 775 776 // TIME and TIME+ use combined value, ksort needs 'em added. 777 slot[SLOT_utime] += slot[SLOT_stime]; 778 slot[SLOT_utime2] = slot[SLOT_utime]; 779 780 // Do we need to read "status"? 781 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP 782 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len) 783 { 784 off_t temp = len; 785 786 sprintf(buf, "%lld/status", slot[SLOT_tid]); 787 if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 788 s = strafter(buf, "\nUid:"); 789 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid; 790 s = strafter(buf, "\nGid:"); 791 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid; 792 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024; 793 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024; 794 } 795 796 // Do we need to read "io"? 797 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) { 798 off_t temp = len; 799 800 sprintf(buf, "%lld/io", slot[SLOT_tid]); 801 if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 802 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s); 803 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s); 804 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s); 805 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s); 806 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap]; 807 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap]; 808 } 809 810 // If we were updating thread parent with its own task info, we're done. 811 if (slot != tb->slot) return 0; 812 813 // We now know enough to skip processes we don't care about. 814 if (TT.match_process && !TT.match_process(slot)) return 0; 815 816 // /proc data is generated as it's read, so for maximum accuracy on slow 817 // systems (or ps | more) we re-fetch uptime as we fetch each /proc line. 818 sysinfo(&si); 819 slot[SLOT_uptime] = si.uptime; 820 slot[SLOT_totalram] = si.totalram; 821 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime]; 822 823 // Do we need to read "statm"? 824 if (TT.bits&(_PS_VIRT|_PS_SHR)) { 825 off_t temp = len; 826 827 sprintf(buf, "%lld/statm", slot[SLOT_tid]); 828 if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 829 830 // Skip redundant RSS field, we got it from stat. 831 slot[SLOT_vsz] = slot[SLOT_shr] = 0; 832 sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]); 833 } 834 835 // Do we need to read "exe"? 836 if (TT.bits&_PS_BIT) { 837 off_t temp = 6; 838 839 sprintf(buf, "%lld/exe", slot[SLOT_tid]); 840 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) { 841 if (buf[4] == 1) slot[SLOT_bits] = 32; 842 else if (buf[4] == 2) slot[SLOT_bits] = 64; 843 } 844 } 845 846 // Do we need Android scheduling policy? 847 if (TT.bits&_PS_PCY) 848 get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]); 849 850 // Done using buf[] (tb->str) as scratch space, now read string data, 851 // saving consective null terminated strings. (Save starting offsets into 852 // str->offset to avoid strlen() loop to find relevant string.) 853 854 // Fetch string data while parentfd still available, appending to buf. 855 // (There's well over 3k of toybuf left. We could dynamically malloc, but 856 // it'd almost never get used, querying length of a proc file is awkward, 857 // fixed buffer is nommu friendly... Wait for somebody to complain. :) 858 859 // The fetch[] array at the start of the function says what file to read 860 // and what -o display field outputs it (to skip the ones we don't need). 861 862 slot[SLOT_argv0len] = 0; 863 for (j = 0; j<ARRAY_LEN(fetch); j++) { 864 tb->offset[j] = buf-(tb->str); 865 if (!(TT.bits&fetch[j].bits)) { 866 *buf++ = 0; 867 continue; 868 } 869 870 // Determine available space: reserve 256 bytes (guaranteed minimum) for 871 // each string we haven't checked yet, tb->str starts after the numeric 872 // arrays in struct procpid, and we reserve 260 bytes scratch space at the 873 // end of toybuf for output conversion in string_field(). Other than that, 874 // each use all available space, and future strings that don't use their 875 // guaranteed minimum add to the pool. 876 len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260; 877 sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name); 878 879 // For exe (j==3) readlink() instead of reading file's contents 880 // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID). 881 if (j==3 || j==5) { 882 struct procpid *ptb = 0; 883 int k; 884 885 // Thread doesn't have exe or argv[0], so use parent's 886 if (TT.threadparent && TT.threadparent->extra) 887 ptb = (void *)TT.threadparent->extra; 888 889 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len); 890 else { 891 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]); 892 else { 893 if (!ptb || slot[SLOT_argv0len]) ptb = tb; 894 i = ptb->slot[SLOT_argv0len]; 895 s = ptb->str+ptb->offset[4]; 896 while (-1!=(k = stridx(s, '/')) && k<i) { 897 s += k+1; 898 i -= k+1; 899 } 900 } 901 if (i<len) len = i; 902 memcpy(buf, s, len); 903 buf[len] = 0; 904 } 905 906 // Turning stat's SLOT_ttynr into a string is an outright heuristic ordeal. 907 } else if (!j) { 908 int rdev = slot[SLOT_ttynr]; 909 struct stat st; 910 911 // Call no tty "?" rather than "0:0". 912 strcpy(buf, "?"); 913 if (rdev) { 914 // Can we readlink() our way to a name? 915 for (i = 0; i<3; i++) { 916 sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i); 917 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode) 918 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len))) 919 break; 920 } 921 922 // Couldn't find it, try all the tty drivers. 923 if (i == 3) { 924 FILE *fp = fopen("/proc/tty/drivers", "r"); 925 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev); 926 927 if (fp) { 928 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) { 929 // TODO: we could parse the minor range too. 930 if (tty_major == maj) { 931 len = strlen(buf); 932 len += sprintf(buf+len, "%d", min); 933 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev) 934 break; 935 } 936 tty_major = 0; 937 } 938 fclose(fp); 939 } 940 941 // Really couldn't find it, so just show major:minor. 942 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min); 943 } 944 945 s = buf; 946 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4); 947 } 948 949 // For the rest, the data we want is in a file we can just read. 950 } else { 951 int temp = 0; 952 953 // When command has no arguments, don't space over the NUL 954 if (readfileat(fd, buf, buf, &len) && len>0) { 955 956 // Trim trailing whitespace and NUL bytes 957 while (len) 958 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0; 959 else break; 960 961 // Turn NUL to space, other low ascii to ? (in non-tty mode), except 962 // cmdline has a trailing NUL that we don't want to turn to space. 963 for (i=0; i<len-1; i++) { 964 char c = buf[i]; 965 966 if (!c) { 967 if (!temp) temp = i; 968 c = ' '; 969 } else if (!TT.tty && c<' ') c = '?'; 970 buf[i] = c; 971 } 972 } else *buf = len = 0; 973 974 // Store end of argv[0] so ARGS and CMDLINE can differ. 975 // We do it for each file string slot but last is cmdline, which sticks. 976 slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL 977 } 978 979 // Each case above calculated/retained len, so we don't need to re-strlen. 980 buf += len+1; 981 } 982 983 // Record that we saw another process, and display/return now if appropriate 984 TT.kcount++; 985 if (TT.show_process && !TT.threadparent) { 986 TT.show_process(tb); 987 988 return 0; 989 } 990 991 // We're retaining data (probably to sort it), save copy in list. 992 s = xmalloc(buf-toybuf); 993 new->extra = (long)s; 994 memcpy(s, toybuf, buf-toybuf); 995 996 return DIRTREE_SAVE; 997} 998 999// wrapper for get_ps() that also collects threads under each processes 1000static int get_threads(struct dirtree *new) 1001{ 1002 struct dirtree *dt; 1003 struct procpid *tb; 1004 unsigned pid, kcount; 1005 1006 if (!new->parent) return get_ps(new); 1007 pid = atol(new->name); 1008 1009 TT.threadparent = new; 1010 if (!get_ps(new)) { 1011 // it exited out from under us 1012 TT.threadparent = 0; 1013 1014 return 0; 1015 } 1016 1017 // Recurse down into tasks, retaining thread groups. 1018 // Disable show_process at least until we can calculate tcount 1019 kcount = TT.kcount; 1020 sprintf(toybuf, "/proc/%u/task", pid); 1021 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); 1022 if (new->child == DIRTREE_ABORTVAL) new->child = 0; 1023 TT.threadparent = 0; 1024 kcount = TT.kcount-kcount+1; 1025 tb = (void *)new->extra; 1026 tb->slot[SLOT_tcount] = kcount; 1027 1028 // Fill out tid and thread count for each entry in group (if it didn't exit 1029 // out from under us again; asynchronous reads of unlocked data are fun!) 1030 if (new->child) for (dt = new->child->child; dt; dt = dt->next) { 1031 tb = (void *)dt->extra; 1032 tb->slot[SLOT_pid] = pid; 1033 tb->slot[SLOT_tcount] = kcount; 1034 } 1035 1036 // Save or display 1037 if (!TT.show_process) return DIRTREE_SAVE; 1038 TT.show_process((void *)new->extra); 1039 if ((dt = new->child)) { 1040 new->child = 0; 1041 while (dt->child) { 1042 new = dt->child->next; 1043 TT.show_process((void *)dt->child->extra); 1044 free(dt->child); 1045 dt->child = new; 1046 } 1047 free(dt); 1048 } 1049 1050 return 0; 1051} 1052 1053// Parse one FIELD argument (with optional =name :width) into struct ofields 1054static char *parse_ko(void *data, char *type, int length) 1055{ 1056 struct ofields *field; 1057 char *width, *title, *end, *s; 1058 int i, j, k; 1059 1060 // Caller's WOULD_EXIT catches -o help and prints help 1061 if (length==4 && !strncasecmp(type, "HELP", length)) xexit(); 1062 1063 // Get title, length of title, type, end of type, and display width 1064 1065 // Chip off =name to display 1066 if ((end = strchr(type, '=')) && length>(end-type)) { 1067 title = end+1; 1068 length -= (end-type)+1; 1069 } else { 1070 end = type+length; 1071 title = 0; 1072 } 1073 1074 // Chip off :width to display 1075 if ((width = strchr(type, ':')) && width<end) { 1076 if (!title) length = width-type; 1077 } else width = 0; 1078 1079 // Allocate structure plus extra space to append a copy of title data 1080 // (this way it's same lifetime, freeing struct automatically frees title) 1081 field = xzalloc(sizeof(struct ofields)+(length+1)*!!title); 1082 if (title) { 1083 memcpy(field->title = (char *)(field+1), title, length); 1084 field->title[field->len = length] = 0; 1085 } 1086 1087 if (width) { 1088 field->len = strtol(++width, &title, 10); 1089 if (!isdigit(*width) || title != end) return title; 1090 end = --width; 1091 } 1092 1093 // Find type 1094 field->reverse = 1; 1095 if (*type == '-') field->reverse = -1; 1096 else if (*type != '+') type--; 1097 type++; 1098 for (i = 0; i<ARRAY_LEN(typos); i++) { 1099 field->which = i; 1100 for (j = 0; j<2; j++) { 1101 if (!j) s = typos[i].name; 1102 // posix requires alternate names for some fields 1103 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU, 1104 PS_VSZ, PS_USER, 0}, i))) continue; 1105 else 1106 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k]; 1107 1108 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break; 1109 } 1110 if (j!=2) break; 1111 } 1112 if (i==ARRAY_LEN(typos)) return type; 1113 if (!field->title) field->title = typos[field->which].name; 1114 if (!field->len) field->len = typos[field->which].width; 1115 else if (typos[field->which].width<0) field->len *= -1; 1116 dlist_add_nomalloc(data, (void *)field); 1117 1118 return 0; 1119} 1120 1121// Write FIELD list into display header string (truncating at blen), 1122// and return bitfield of which FIELDs are used. 1123static long long get_headers(struct ofields *field, char *buf, int blen) 1124{ 1125 long long bits = 0; 1126 int len = 0; 1127 1128 for (; field; field = field->next) { 1129 len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len, 1130 field->title); 1131 bits |= 1LL<<field->which; 1132 } 1133 1134 return bits; 1135} 1136 1137// Parse command line options -p -s -t -u -U -g -G 1138static char *parse_rest(void *data, char *str, int len) 1139{ 1140 struct ptr_len *pl = (struct ptr_len *)data; 1141 long *ll = pl->ptr; 1142 char *end; 1143 int num = 0; 1144 1145 // Allocate next chunk of data 1146 if (!(15&pl->len)) 1147 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16)); 1148 1149 // Parse numerical input 1150 if (isdigit(*str)) { 1151 ll[pl->len] = xstrtol(str, &end, 10); 1152 if (end==(len+str)) num++; 1153 // For pkill, -s 0 represents pkill's session id. 1154 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0); 1155 } 1156 1157 if (pl==&TT.pp || pl==&TT.ss) { 1158 if (num && ll[pl->len]>0) { 1159 pl->len++; 1160 1161 return 0; 1162 } 1163 } else if (pl==&TT.tt) { 1164 // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0 1165 if (!num) { 1166 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5; 1167 if (strstart(&str, "pts/")) { 1168 len -= 4; 1169 num++; 1170 } else if (strstart(&str, "tty")) len -= 3; 1171 } 1172 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) { 1173 struct stat st; 1174 1175 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty"); 1176 memcpy(end, str, len); 1177 end[len] = 0; 1178 xstat(toybuf, &st); 1179 ll[pl->len++] = st.st_rdev; 1180 1181 return 0; 1182 } 1183 } else if (len<255) { 1184 char name[256]; 1185 1186 if (num) { 1187 pl->len++; 1188 1189 return 0; 1190 } 1191 1192 memcpy(name, str, len); 1193 name[len] = 0; 1194 if (pl==&TT.gg || pl==&TT.GG) { 1195 struct group *gr = getgrnam(name); 1196 if (gr) { 1197 ll[pl->len++] = gr->gr_gid; 1198 1199 return 0; 1200 } 1201 } else if (pl==&TT.uu || pl==&TT.UU) { 1202 struct passwd *pw = getpwnam(name); 1203 if (pw) { 1204 ll[pl->len++] = pw->pw_uid; 1205 1206 return 0; 1207 } 1208 } 1209 } 1210 1211 // Return error 1212 return str; 1213} 1214 1215// sort processes by FIELD(s) listed in option -k 1216static int ksort(void *aa, void *bb) 1217{ 1218 struct ofields *field; 1219 struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb; 1220 int ret = 0, slot; 1221 1222 for (field = TT.kfields; field && !ret; field = field->next) { 1223 slot = typos[field->which].slot; 1224 1225 // Can we do numeric sort? 1226 if (!(slot&XX)) { 1227 if (ta->slot[slot]<tb->slot[slot]) ret = -1; 1228 if (ta->slot[slot]>tb->slot[slot]) ret = 1; 1229 } 1230 1231 // fallback to string sort 1232 if (!ret) { 1233 memccpy(toybuf, string_field(ta, field), 0, 2048); 1234 toybuf[2048] = 0; 1235 ret = strcmp(toybuf, string_field(tb, field)); 1236 } 1237 ret *= field->reverse; 1238 } 1239 1240 return ret; 1241} 1242 1243// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array 1244// (recursion because tree from get_thread() isn't flat list of siblings) 1245static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) 1246{ 1247 while (dt) { 1248 struct dirtree *next = dt->next; 1249 1250 if (dt->extra) *(tb++) = (void *)dt->extra; 1251 if (dt->child) tb = collate_leaves(tb, dt->child); 1252 free(dt); 1253 dt = next; 1254 } 1255 1256 return tb; 1257} 1258 1259// Allocate struct procpid array of length count and populate it with ->extra 1260// fields from dirtree leaf nodes. (top diffs old & new array to show changes) 1261static struct procpid **collate(int count, struct dirtree *dt) 1262{ 1263 struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *)); 1264 1265 collate_leaves(tbsort, dt); 1266 1267 return tbsort; 1268} 1269 1270// parse command line arguments (ala -k -o) with a comma separated FIELD list 1271static void default_ko(char *s, void *fields, char *err, struct arg_list *arg) 1272{ 1273 struct arg_list def; 1274 int x; 1275 1276 memset(&def, 0, sizeof(struct arg_list)); 1277 def.arg = s; 1278 WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko)); 1279 if (x) help_help(); 1280} 1281 1282void ps_main(void) 1283{ 1284 char **arg; 1285 struct dirtree *dt; 1286 char *not_o; 1287 int i; 1288 1289 TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime 1290 1291 if (-1 != (i = tty_fd())) { 1292 struct stat st; 1293 1294 if (!fstat(i, &st)) TT.tty = st.st_rdev; 1295 } 1296 1297 // If we can't query terminal size pad to 80 but do -w 1298 TT.width = 80; 1299 if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w; 1300 if (FLAG(w)) TT.width = 99999; 1301 1302 // parse command line options other than -o 1303 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest); 1304 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest); 1305 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest); 1306 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest); 1307 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest); 1308 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest); 1309 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest); 1310 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest); 1311 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko); 1312 dlist_terminate(TT.kfields); 1313 1314 // It's undocumented, but traditionally extra arguments are extra -p args 1315 for (arg = toys.optargs; *arg; arg++) 1316 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg); 1317 1318 // Figure out which fields to display 1319 not_o = "%sTTY,TIME,CMD"; 1320 if (FLAG(f)) 1321 sprintf(not_o = toybuf+128, 1322 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C"); 1323 else if (FLAG(l)) 1324 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD"; 1325 else if (CFG_TOYBOX_ON_ANDROID) 1326 sprintf(not_o = toybuf+128, 1327 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s", 1328 FLAG(T) ? "CMD" : "NAME"); 1329 sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,"); 1330 1331 // Init TT.fields. This only uses toybuf if TT.ps.o is NULL 1332 if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0); 1333 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o); 1334 1335 if (TT.ps.O) { 1336 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev; 1337 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko); 1338 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next; 1339 } 1340 dlist_terminate(TT.fields); 1341 1342 // -f and -n change the meaning of some fields 1343 if (FLAG(f)||FLAG(n)) { 1344 struct ofields *field; 1345 1346 for (field = TT.fields; field; field = field->next) { 1347 if (FLAG(n) && field->which>=PS_UID 1348 && field->which<=PS_RGROUP && (typos[field->which].slot&XX)) 1349 field->which--; 1350 } 1351 } 1352 1353 // Calculate seen fields bit array, and if we aren't deferring printing 1354 // print headers now (for low memory/nommu systems). 1355 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf)); 1356 if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf); 1357 if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps; 1358 TT.match_process = ps_match_process; 1359 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, 1360 (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT))) 1361 ? get_threads : get_ps); 1362 1363 if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) { 1364 struct procpid **tbsort = collate(TT.kcount, dt); 1365 1366 if (FLAG(M)) { 1367 for (i = 0; i<TT.kcount; i++) { 1368 struct ofields *field; 1369 1370 for (field = TT.fields; field; field = field->next) { 1371 int len = strlen(string_field(tbsort[i], field)); 1372 1373 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len; 1374 } 1375 } 1376 1377 // Now that we've recalculated field widths, re-pad headers again 1378 get_headers(TT.fields, toybuf, sizeof(toybuf)); 1379 printf("%.*s\n", TT.width, toybuf); 1380 } 1381 1382 if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort); 1383 for (i = 0; i<TT.kcount; i++) { 1384 show_ps(tbsort[i]); 1385 free(tbsort[i]); 1386 } 1387 if (CFG_TOYBOX_FREE) free(tbsort); 1388 } 1389 1390 if (CFG_TOYBOX_FREE) { 1391 free(TT.gg.ptr); 1392 free(TT.GG.ptr); 1393 free(TT.pp.ptr); 1394 free(TT.PP.ptr); 1395 free(TT.ss.ptr); 1396 free(TT.tt.ptr); 1397 free(TT.uu.ptr); 1398 free(TT.UU.ptr); 1399 llist_traverse(TT.fields, free); 1400 } 1401} 1402 1403#define CLEANUP_ps 1404#define FOR_top 1405#include "generated/flags.h" 1406 1407// select which of the -o fields to sort by 1408static void setsort(int pos) 1409{ 1410 struct ofields *field, *field2; 1411 int i = 0; 1412 1413 if (pos<0) pos = 0; 1414 1415 for (field = TT.fields; field; field = field->next) { 1416 if ((TT.sortpos = i++)<pos && field->next) continue; 1417 field2 = TT.kfields; 1418 field2->which = field->which; 1419 field2->len = field->len; 1420 break; 1421 } 1422} 1423 1424// If we have both, adjust slot[deltas[]] to be relative to previous 1425// measurement rather than process start. Stomping old.data is fine 1426// because we free it after displaying. 1427static int merge_deltas(long long *oslot, long long *nslot, int milis) 1428{ 1429 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar, 1430 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap}; 1431 int i; 1432 1433 for (i = 0; i<ARRAY_LEN(deltas); i++) 1434 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]]; 1435 oslot[SLOT_upticks] = (milis*TT.ticks)/1000; 1436 1437 return 1; 1438} 1439 1440static int header_line(int line, int rev) 1441{ 1442 if (!line) return 0; 1443 1444 if (FLAG(b)) puts(toybuf); 1445 else { 1446 printf("%s%-*.*s%s\r\n", rev?"\033[7m":"", rev?TT.width:0, TT.width, toybuf, 1447 rev?"\033[0m":""); 1448 } 1449 1450 return line-1; 1451} 1452 1453static void top_cursor_cleanup(void) 1454{ 1455 tty_esc("?25h"); 1456} 1457 1458static void top_common( 1459 int (*filter)(long long *oslot, long long *nslot, int milis)) 1460{ 1461 long long timeout = 0, now, stats[16]; 1462 struct proclist { 1463 struct procpid **tb; 1464 int count; 1465 long long whence; 1466 } plist[2], *plold, *plnew, old, new, mix; 1467 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle", 1468 "iow", "irq", "sirq", "host"}; 1469 unsigned tock = 0; 1470 int i, lines, topoff = 0, done = 0; 1471 char stdout_buf[8192]; 1472 1473 if (!TT.fields) perror_exit("no -o"); 1474 1475 // Avoid flicker and hide the cursor in interactive mode. 1476 if (!FLAG(b)) { 1477 setbuffer(stdout, stdout_buf, sizeof(stdout_buf)); 1478 sigatexit(top_cursor_cleanup); 1479 tty_esc("?25l"); 1480 } 1481 1482 toys.signal = SIGWINCH; 1483 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf)); 1484 *scratch = 0; 1485 memset(plist, 0, sizeof(plist)); 1486 memset(stats, 0, sizeof(stats)); 1487 do { 1488 struct dirtree *dt; 1489 int recalc = 1; 1490 1491 plold = plist+(tock++&1); 1492 plnew = plist+(tock&1); 1493 plnew->whence = millitime(); 1494 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, 1495 (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps); 1496 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc"); 1497 plnew->tb = collate(plnew->count = TT.kcount, dt); 1498 TT.kcount = 0; 1499 1500 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) { 1501 long long *st = stats+8*(tock&1); 1502 1503 // user nice system idle iowait irq softirq host 1504 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", 1505 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7); 1506 } 1507 1508 // First time, wait a quarter of a second to collect a little delta data. 1509 if (!plold->tb) { 1510 msleep(250); 1511 continue; 1512 } 1513 1514 // Collate old and new into "mix", depends on /proc read in pid sort order 1515 old = *plold; 1516 new = *plnew; 1517 mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid)); 1518 mix.count = 0; 1519 1520 int merge_idx = (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? SLOT_tid : SLOT_pid; 1521 while (old.count || new.count) { 1522 struct procpid *otb = old.count ? *old.tb : 0, 1523 *ntb = new.count ? *new.tb : 0; 1524 1525 // If we just have old for this process, it exited. Discard it. 1526 if (old.count && (!new.count || otb->slot[merge_idx] < ntb->slot[merge_idx])) { 1527 old.tb++; 1528 old.count--; 1529 1530 continue; 1531 } 1532 1533 // If we just have new, use it verbatim 1534 if (!old.count || otb->slot[merge_idx] > ntb->slot[merge_idx]) mix.tb[mix.count] = ntb; 1535 else { 1536 // Keep or discard 1537 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) { 1538 mix.tb[mix.count] = otb; 1539 mix.count++; 1540 } 1541 old.tb++; 1542 old.count--; 1543 } 1544 new.tb++; 1545 new.count--; 1546 } 1547 1548 // Don't re-fetch data if it's not time yet, just re-display existing data. 1549 for (;;) { 1550 char was, is; 1551 1552 if (recalc) { 1553 qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort); 1554 if (!FLAG(b)) { 1555 printf("\033[H\033[J"); 1556 if (toys.signal) { 1557 toys.signal = 0; 1558 terminal_probesize(&TT.width, &TT.height); 1559 } 1560 } 1561 if (TT.top.m) TT.height = TT.top.m+5; 1562 lines = TT.height; 1563 } 1564 if (recalc && !FLAG(q)) { 1565 // Display "top" header. 1566 if (*toys.which->name == 't') { 1567 struct ofields field; 1568 char hr[4][32]; 1569 long long ll, up = 0; 1570 long run[6]; 1571 int j; 1572 1573 // Count running, sleeping, stopped, zombie processes. 1574 // The kernel has more states (and different sets in different 1575 // versions), so we need to map them. (R)unning and (Z)ombie are 1576 // easy enough, and since "stopped" is rare (just T and t as of 1577 // Linux 4.20), we assume everything else is "sleeping". 1578 field.which = PS_S; 1579 memset(run, 0, sizeof(run)); 1580 for (i = 0; i<mix.count; i++) 1581 run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++; 1582 sprintf(toybuf, 1583 "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, " 1584 "%3ld zombie", FLAG(H)?"Thread":"Task", mix.count, run[1], run[0], 1585 run[2]+run[3], run[4]); 1586 lines = header_line(lines, 0); 1587 1588 if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) { 1589 for (i = 0; i<6; i++) { 1590 j = i%3; 1591 pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:", 1592 "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]); 1593 human_readable_long(hr[j+!!j], 1024*(run[i] = pos?atol(pos):0), 1594 8, 0); 1595 if (j==1) human_readable_long(hr[1], 1024*(run[i-1]-run[i]), 8,0); 1596 else if (j==2) { 1597 sprintf(toybuf, (i<3) 1598 ? " Mem: %9s total, %9s used, %9s free, %9s buffers" 1599 : " Swap: %9s total, %9s used, %9s free, %9s cached", 1600 hr[0], hr[1], hr[2], hr[3]); 1601 lines = header_line(lines, 0); 1602 } 1603 } 1604 } 1605 1606 pos = toybuf; 1607 i = sysconf(_SC_NPROCESSORS_CONF); 1608 pos += sprintf(pos, "%d%%cpu", i*100); 1609 j = 4+(i>10); 1610 1611 // If a processor goes idle it's powered down and its idle ticks don't 1612 // advance, so calculate idle time as potential time - used. 1613 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks]; 1614 if (!up) { 1615 up = 1; 1616 memset(stats, 0, sizeof(stats)); 1617 } 1618 now = up*i; 1619 ll = stats[3] = stats[11] = 0; 1620 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8]; 1621 stats[3] = now - llabs(ll); 1622 1623 for (i = 0; i<8; i++) { 1624 ll = (llabs(stats[i]-stats[i+8])*1000)/up; 1625 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]); 1626 } 1627 lines = header_line(lines, 0); 1628 } else { 1629 struct ofields *field; 1630 struct procpid tb; 1631 1632 memset(&tb, 0, sizeof(struct procpid)); 1633 pos = stpcpy(toybuf, "Totals:"); 1634 for (field = TT.fields; field; field = field->next) { 1635 long long ll, bits = 0; 1636 int slot = typos[field->which].slot&(XX-1); 1637 1638 if (field->which<PS_C || field->which>PS_DIO) continue; 1639 ll = 1LL<<field->which; 1640 if (bits&ll) continue; 1641 bits |= ll; 1642 for (i=0; i<mix.count; i++) 1643 tb.slot[slot] += mix.tb[i]->slot[slot]; 1644 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf), 1645 " %s: %*s,", typos[field->which].name, 1646 field->len, string_field(&tb, field)); 1647 } 1648 *--pos = 0; 1649 lines = header_line(lines, 0); 1650 } 1651 1652 get_headers(TT.fields, pos = toybuf, sizeof(toybuf)); 1653 for (i = 0, is = ' '; *pos; pos++) { 1654 was = is; 1655 is = *pos; 1656 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf) 1657 pos[-1] = '['; 1658 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']'; 1659 } 1660 if (FLAG(b)) while (isspace(*(pos-1))) --pos; 1661 *pos = 0; 1662 lines = header_line(lines, 1); 1663 } 1664 if (!recalc && !FLAG(b)) 1665 printf("\033[%dH\033[J", 1+TT.height-lines); 1666 recalc = 1; 1667 1668 for (i = 0; i<lines && i+topoff<mix.count; i++) { 1669 // Running processes are shown in bold. 1670 int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R'; 1671 1672 if (!FLAG(b) && i) putchar('\n'); 1673 if (bold) printf("\033[1m"); 1674 show_ps(mix.tb[i+topoff]); 1675 if (bold) printf("\033[m"); 1676 } 1677 1678 if (TT.top.n && !--TT.top.n) { 1679 done++; 1680 break; 1681 } 1682 1683 now = millitime(); 1684 if (timeout<=now) timeout = new.whence+TT.top.d; 1685 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d; 1686 1687 // In batch mode, we ignore the keyboard. 1688 if (FLAG(b)) { 1689 msleep(timeout-now); 1690 // Make an obvious gap between datasets. 1691 xputs("\n\n"); 1692 break; 1693 } else fflush(stdout); 1694 1695 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height); 1696 if (i==-1 || i==3 || toupper(i)=='Q') { 1697 done++; 1698 break; 1699 } 1700 if (i==-2) break; 1701 if (i==-3) continue; 1702 1703 // Flush unknown escape sequences. 1704 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height)); 1705 else if (i=='\r' || i==' ') { 1706 timeout = 0; 1707 break; 1708 } else if (toupper(i)=='R') 1709 ((struct ofields *)TT.kfields)->reverse *= -1; 1710 else { 1711 i -= 256; 1712 if (i == KEY_LEFT) setsort(TT.sortpos-1); 1713 else if (i == KEY_RIGHT) setsort(TT.sortpos+1); 1714 // KEY_UP is 0, so at end of strchr 1715 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) { 1716 recalc = 0; 1717 1718 if (i == KEY_UP) topoff--; 1719 else if (i == KEY_DOWN) topoff++; 1720 else if (i == KEY_PGDN) topoff += lines; 1721 else if (i == KEY_PGUP) topoff -= lines; 1722 if (topoff<0) topoff = 0; 1723 if (topoff>mix.count) topoff = mix.count; 1724 } 1725 } 1726 continue; 1727 } 1728 1729 free(mix.tb); 1730 for (i=0; i<plold->count; i++) free(plold->tb[i]); 1731 free(plold->tb); 1732 } while (!done); 1733 1734 if (!FLAG(b)) tty_reset(); 1735} 1736 1737static void top_setup(char *defo, char *defk) 1738{ 1739 TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime 1740 TT.tty = tty_fd() != -1; 1741 1742 // Are we doing "batch" output or interactive? 1743 if (FLAG(b)) TT.width = TT.height = 99999; 1744 else { 1745 // Grab starting time, make terminal raw, switch off cursor, 1746 // set signal handler to put terminal/cursor back to normal at exit. 1747 TT.time = millitime(); 1748 start_redraw(&TT.width, &TT.height); 1749 } 1750 1751 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest); 1752 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest); 1753 TT.match_process = shared_match_process; 1754 1755 default_ko(defo, &TT.fields, "bad -o", TT.top.o); 1756 dlist_terminate(TT.fields); 1757 1758 // First (dummy) sort field is overwritten by setsort() 1759 default_ko("-S", &TT.kfields, 0, 0); 1760 default_ko(defk, &TT.kfields, "bad -k", TT.top.k); 1761 dlist_terminate(TT.kfields); 1762 setsort(TT.top.s-1); 1763} 1764 1765void top_main(void) 1766{ 1767 sprintf(toybuf, "%cID,USER,%s%%CPU,%%MEM,TIME+,%s", FLAG(H) ? 'T' : 'P', 1768 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,", 1769 FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS"); 1770 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9; 1771 top_setup(toybuf, "-%CPU,-ETIME,-PID"); 1772 if (TT.top.O) { 1773 struct ofields *field = TT.fields; 1774 1775 field = field->next->next; 1776 comma_args(TT.top.O, &field, "bad -O", parse_ko); 1777 } 1778 1779 top_common(merge_deltas); 1780} 1781 1782#define CLEANUP_top 1783#define FOR_iotop 1784#include "generated/flags.h" 1785 1786// Compare old and new proces lists to measure changes 1787static int iotop_filter(long long *oslot, long long *nslot, int milis) 1788{ 1789 // Current I/O, or accumulated since process start? 1790 if (!FLAG(a)) merge_deltas(oslot, nslot, milis); 1791 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000; 1792 1793 return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)]; 1794} 1795 1796void iotop_main(void) 1797{ 1798 char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A); 1799 1800 if (FLAG(K)) TT.forcek++; 1801 1802 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d), 1803 s2 = xmprintf("-%sIO,-ETIME,-PID",d)); 1804 free(s1); 1805 free(s2); 1806 top_common(iotop_filter); 1807} 1808 1809// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag 1810// context, so force pgrep's flags on even when building pkill standalone. 1811// (All the pgrep/pkill functions drop out when building ps standalone.) 1812#define FORCE_FLAGS 1813#define CLEANUP_iotop 1814#define FOR_pgrep 1815#include "generated/flags.h" 1816 1817struct regex_list { 1818 struct regex_list *next; 1819 regex_t reg; 1820}; 1821 1822static void do_pgk(struct procpid *tb) 1823{ 1824 if (TT.pgrep.signal) { 1825 if (kill(*tb->slot, TT.pgrep.signal)) { 1826 char *s = num_to_sig(TT.pgrep.signal); 1827 1828 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal); 1829 perror_msg("%s->%lld", s, *tb->slot); 1830 } 1831 } 1832 if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) { 1833 printf("%lld", *tb->slot); 1834 if (FLAG(l)) 1835 printf(" %s", tb->str+tb->offset[4]*!!FLAG(f)); 1836 1837 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n"); 1838 } 1839} 1840 1841static void match_pgrep(void *p) 1842{ 1843 struct procpid *tb = p; 1844 regmatch_t match; 1845 struct regex_list *reg; 1846 char *name = tb->str+tb->offset[4]*!!FLAG(f); 1847 1848 // Never match ourselves. 1849 if (TT.pgrep.self == *tb->slot) return; 1850 1851 if (TT.pgrep.regexes) { 1852 for (reg = TT.pgrep.regexes; reg; reg = reg->next) { 1853 if (regexec(®->reg, name, 1, &match, 0)) continue; 1854 if (FLAG(x)) 1855 if (match.rm_so || match.rm_eo!=strlen(name)) continue; 1856 break; 1857 } 1858 if (!FLAG(v) == !reg) return; 1859 } 1860 1861 // pgrep should return success if there's a match. 1862 toys.exitval = 0; 1863 1864 // Repurpose a field for -c count. 1865 TT.sortpos++; 1866 if (FLAG(n)||FLAG(o)) { 1867 long long ll = tb->slot[SLOT_starttime]; 1868 1869 if (FLAG(o)) ll *= -1; 1870 if (TT.time && TT.time>ll) return; 1871 TT.time = ll; 1872 free(TT.pgrep.snapshot); 1873 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf); 1874 } else do_pgk(tb); 1875} 1876 1877static int pgrep_match_process(long long *slot) 1878{ 1879 return !FLAG(v) == !!shared_match_process(slot); 1880} 1881 1882void pgrep_main(void) 1883{ 1884 char **arg; 1885 struct regex_list *reg; 1886 1887 TT.pgrep.self = getpid(); 1888 1889 // No signal names start with "L", so no need for "L: " in optstr. 1890 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L))) 1891 error_exit("bad -L '%s'", TT.pgrep.L); 1892 1893 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest); 1894 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest); 1895 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest); 1896 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest); 1897 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest); 1898 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest); 1899 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest); 1900 1901 if ((toys.optflags&(FLAG_x|FLAG_f)) || 1902 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u))) 1903 if (!toys.optc) help_exit("No PATTERN"); 1904 1905 if (FLAG(f)) TT.bits |= _PS_CMDLINE; 1906 for (arg = toys.optargs; *arg; arg++) { 1907 reg = xmalloc(sizeof(struct regex_list)); 1908 xregcomp(®->reg, *arg, REG_EXTENDED); 1909 reg->next = TT.pgrep.regexes; 1910 TT.pgrep.regexes = reg; 1911 } 1912 TT.match_process = pgrep_match_process; 1913 TT.show_process = match_pgrep; 1914 1915 // pgrep should return failure if there are no matches. 1916 toys.exitval = 1; 1917 1918 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); 1919 if (FLAG(c)) printf("%d\n", TT.sortpos); 1920 if (TT.pgrep.snapshot) { 1921 do_pgk(TT.pgrep.snapshot); 1922 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot); 1923 } 1924 if (TT.pgrep.d) xputc('\n'); 1925} 1926 1927#define CLEANUP_pgrep 1928#define FOR_pkill 1929#include "generated/flags.h" 1930 1931void pkill_main(void) 1932{ 1933 char **args = toys.optargs; 1934 1935 if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1; 1936 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM; 1937 if (FLAG(V)) TT.tty = 1; 1938 pgrep_main(); 1939} 1940