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