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