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