10f66f451Sopenharmony_ci/* ps.c - show process list 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2015 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html 60f66f451Sopenharmony_ci * And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4 70f66f451Sopenharmony_ci * And linux kernel source fs/proc/array.c function do_task_stat() 80f66f451Sopenharmony_ci * 90f66f451Sopenharmony_ci * Deviations from posix: no -n because /proc/self/wchan exists; we use -n to 100f66f451Sopenharmony_ci * mean "show numeric users and groups" instead. 110f66f451Sopenharmony_ci * Posix says default output should have field named "TTY" but if you "-o tty" 120f66f451Sopenharmony_ci * the same field should be called "TT" which is _INSANE_ and I'm not doing it. 130f66f451Sopenharmony_ci * Similarly -f outputs USER but calls it UID (we call it USER). 140f66f451Sopenharmony_ci * It also says that -o "args" and "comm" should behave differently but use 150f66f451Sopenharmony_ci * the same title, which is not the same title as the default output. (No.) 160f66f451Sopenharmony_ci * Select by session id is -s not -g. Posix doesn't say truncated fields 170f66f451Sopenharmony_ci * should end with "+" but it's pretty common behavior. 180f66f451Sopenharmony_ci * 190f66f451Sopenharmony_ci * Posix defines -o ADDR as "The address of the process" but the process 200f66f451Sopenharmony_ci * start address is a constant on any elf system with mmu. The procps ADDR 210f66f451Sopenharmony_ci * field always prints "-" with an alignment of 1, which is why it has 11 220f66f451Sopenharmony_ci * characters left for "cmd" in in 80 column "ps -l" mode. On x86-64 you 230f66f451Sopenharmony_ci * need 12 chars, leaving nothing for cmd: I.E. posix 2008 ps -l mode can't 240f66f451Sopenharmony_ci * be sanely implemented on 64 bit Linux systems. In procps there's ps -y 250f66f451Sopenharmony_ci * which changes -l by removing the "F" column and swapping RSS for ADDR, 260f66f451Sopenharmony_ci * leaving 9 chars for cmd, so we're using that as our -l output. 270f66f451Sopenharmony_ci * 280f66f451Sopenharmony_ci * Added a bunch of new -o fields posix doesn't mention, and we don't 290f66f451Sopenharmony_ci * label "ps -o command,args,comm" as "COMMAND COMMAND COMMAND". We don't 300f66f451Sopenharmony_ci * output argv[0] unmodified for -o comm or -o args (but procps violates 310f66f451Sopenharmony_ci * posix for -o comm anyway, it's stat[2] not argv[0]). 320f66f451Sopenharmony_ci * 330f66f451Sopenharmony_ci * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io 340f66f451Sopenharmony_ci * files (why they're not globally readable when the rest of proc 350f66f451Sopenharmony_ci * data is...?) and get a global I/O picture. Normal top is NOT, 360f66f451Sopenharmony_ci * even though you can -o AIO there, to give sysadmins the option 370f66f451Sopenharmony_ci * to reduce security exposure.) 380f66f451Sopenharmony_ci * 390f66f451Sopenharmony_ci * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference) 400f66f451Sopenharmony_ci * TODO: switch -fl to -y 410f66f451Sopenharmony_ci * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l") 420f66f451Sopenharmony_ci * TODO: iotop: Window size change: respond immediately. Why not padding 430f66f451Sopenharmony_ci * at right edge? (Not adjusting to screen size at all? Header wraps?) 440f66f451Sopenharmony_ci * TODO: top: thread support and SMP 450f66f451Sopenharmony_ci * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf. 460f66f451Sopenharmony_ci 470f66f451Sopenharmony_ciUSE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ#<1a[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE)) 480f66f451Sopenharmony_ci// stayroot because iotop needs root to read other process' proc/$$/io 490f66f451Sopenharmony_ci// TOP and IOTOP have a large common option block used for common processing, 500f66f451Sopenharmony_ci// the default values are different but the flags are in the same order. 510f66f451Sopenharmony_ciUSE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d%<100=3000m#n#<1abq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) 520f66f451Sopenharmony_ciUSE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d%<100=3000m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE)) 530f66f451Sopenharmony_ciUSE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) 540f66f451Sopenharmony_ciUSE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN)) 550f66f451Sopenharmony_ci 560f66f451Sopenharmony_ciconfig PS 570f66f451Sopenharmony_ci bool "ps" 580f66f451Sopenharmony_ci default y 590f66f451Sopenharmony_ci help 600f66f451Sopenharmony_ci usage: ps or ps -a 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ciconfig TOP 630f66f451Sopenharmony_ci bool "top" 640f66f451Sopenharmony_ci default y 650f66f451Sopenharmony_ci help 660f66f451Sopenharmony_ci usage: top or top -a 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci# Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io 690f66f451Sopenharmony_ciconfig IOTOP 700f66f451Sopenharmony_ci bool "iotop" 710f66f451Sopenharmony_ci default n 720f66f451Sopenharmony_ci help 730f66f451Sopenharmony_ci usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_ci Rank processes by I/O. 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ci -A All I/O, not just disk 780f66f451Sopenharmony_ci -a Accumulated I/O (not percentage) 790f66f451Sopenharmony_ci -H Show threads 800f66f451Sopenharmony_ci -K Kilobytes 810f66f451Sopenharmony_ci -k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID) 820f66f451Sopenharmony_ci -m Maximum number of tasks to show 830f66f451Sopenharmony_ci -O Only show processes doing I/O 840f66f451Sopenharmony_ci -o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM) 850f66f451Sopenharmony_ci -s Sort by field number (0-X, default 6) 860f66f451Sopenharmony_ci -b Batch mode (no tty) 870f66f451Sopenharmony_ci -d Delay SECONDS between each cycle (default 3) 880f66f451Sopenharmony_ci -n Exit after NUMBER iterations 890f66f451Sopenharmony_ci -p Show these PIDs 900f66f451Sopenharmony_ci -u Show these USERs 910f66f451Sopenharmony_ci -q Quiet (no header lines) 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force 940f66f451Sopenharmony_ci update, R to reverse sort, Q to exit. 950f66f451Sopenharmony_ci 960f66f451Sopenharmony_ciconfig PGREP 970f66f451Sopenharmony_ci bool "pgrep" 980f66f451Sopenharmony_ci default n 990f66f451Sopenharmony_ci help 1000f66f451Sopenharmony_ci usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] 1010f66f451Sopenharmony_ci 1020f66f451Sopenharmony_ci Search for process(es). PATTERN is an extended regular expression checked 1030f66f451Sopenharmony_ci against command names. 1040f66f451Sopenharmony_ci 1050f66f451Sopenharmony_ci -c Show only count of matches 1060f66f451Sopenharmony_ci -d Use DELIM instead of newline 1070f66f451Sopenharmony_ci -L Send SIGNAL instead of printing name 1080f66f451Sopenharmony_ci -l Show command name 1090f66f451Sopenharmony_ci -f Check full command line for PATTERN 1100f66f451Sopenharmony_ci -G Match real Group ID(s) 1110f66f451Sopenharmony_ci -g Match Process Group(s) (0 is current user) 1120f66f451Sopenharmony_ci -n Newest match only 1130f66f451Sopenharmony_ci -o Oldest match only 1140f66f451Sopenharmony_ci -P Match Parent Process ID(s) 1150f66f451Sopenharmony_ci -s Match Session ID(s) (0 for current) 1160f66f451Sopenharmony_ci -t Match Terminal(s) 1170f66f451Sopenharmony_ci -U Match real User ID(s) 1180f66f451Sopenharmony_ci -u Match effective User ID(s) 1190f66f451Sopenharmony_ci -v Negate the match 1200f66f451Sopenharmony_ci -x Match whole command (not substring) 1210f66f451Sopenharmony_ci 1220f66f451Sopenharmony_ciconfig PKILL 1230f66f451Sopenharmony_ci bool "pkill" 1240f66f451Sopenharmony_ci default n 1250f66f451Sopenharmony_ci help 1260f66f451Sopenharmony_ci usage: pkill [-fnovx] [-SIGNAL|-l SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] 1270f66f451Sopenharmony_ci 1280f66f451Sopenharmony_ci -l Send SIGNAL (default SIGTERM) 1290f66f451Sopenharmony_ci -V Verbose 1300f66f451Sopenharmony_ci -f Check full command line for PATTERN 1310f66f451Sopenharmony_ci -G Match real Group ID(s) 1320f66f451Sopenharmony_ci -g Match Process Group(s) (0 is current user) 1330f66f451Sopenharmony_ci -n Newest match only 1340f66f451Sopenharmony_ci -o Oldest match only 1350f66f451Sopenharmony_ci -P Match Parent Process ID(s) 1360f66f451Sopenharmony_ci -s Match Session ID(s) (0 for current) 1370f66f451Sopenharmony_ci -t Match Terminal(s) 1380f66f451Sopenharmony_ci -U Match real User ID(s) 1390f66f451Sopenharmony_ci -u Match effective User ID(s) 1400f66f451Sopenharmony_ci -v Negate the match 1410f66f451Sopenharmony_ci -x Match whole command (not substring) 1420f66f451Sopenharmony_ci*/ 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci#define FOR_ps 1450f66f451Sopenharmony_ci#include "toys.h" 1460f66f451Sopenharmony_ci 1470f66f451Sopenharmony_ciGLOBALS( 1480f66f451Sopenharmony_ci union { 1490f66f451Sopenharmony_ci struct { 1500f66f451Sopenharmony_ci struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k; 1510f66f451Sopenharmony_ci } ps; 1520f66f451Sopenharmony_ci struct { 1530f66f451Sopenharmony_ci long n, m, d, s; 1540f66f451Sopenharmony_ci struct arg_list *u, *p, *o, *k, *O; 1550f66f451Sopenharmony_ci } top; 1560f66f451Sopenharmony_ci struct { 1570f66f451Sopenharmony_ci char *L; 1580f66f451Sopenharmony_ci struct arg_list *G, *g, *P, *s, *t, *U, *u; 1590f66f451Sopenharmony_ci char *d; 1600f66f451Sopenharmony_ci 1610f66f451Sopenharmony_ci void *regexes, *snapshot; 1620f66f451Sopenharmony_ci int signal; 1630f66f451Sopenharmony_ci pid_t self, match; 1640f66f451Sopenharmony_ci } pgrep; 1650f66f451Sopenharmony_ci }; 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_ci struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU; 1680f66f451Sopenharmony_ci struct dirtree *threadparent; 1690f66f451Sopenharmony_ci unsigned width, height; 1700f66f451Sopenharmony_ci dev_t tty; 1710f66f451Sopenharmony_ci void *fields, *kfields; 1720f66f451Sopenharmony_ci long long ticks, bits, time; 1730f66f451Sopenharmony_ci int kcount, forcek, sortpos; 1740f66f451Sopenharmony_ci int (*match_process)(long long *slot); 1750f66f451Sopenharmony_ci void (*show_process)(void *tb); 1760f66f451Sopenharmony_ci) 1770f66f451Sopenharmony_ci 1780f66f451Sopenharmony_ci// Linked list of -o fields selected for display, in order, with :len and =title 1790f66f451Sopenharmony_ci 1800f66f451Sopenharmony_cistruct ofields { 1810f66f451Sopenharmony_ci struct ofields *next, *prev; 1820f66f451Sopenharmony_ci short which, len, reverse; 1830f66f451Sopenharmony_ci char *title; 1840f66f451Sopenharmony_ci}; 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci/* The function get_ps() reads all the data about one process, saving it in 1870f66f451Sopenharmony_ci * toybox as a struct procpid. Simple ps calls then pass toybuf directly to 1880f66f451Sopenharmony_ci * show_ps(), but features like sorting append a copy to a linked list 1890f66f451Sopenharmony_ci * for further processing once all processes have been read. 1900f66f451Sopenharmony_ci * 1910f66f451Sopenharmony_ci * struct procpid contains a slot[] array of 64 bit values, with the following 1920f66f451Sopenharmony_ci * data at each position in the array. Most is read from /proc/$PID/stat (see 1930f66f451Sopenharmony_ci * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but 1940f66f451Sopenharmony_ci * we replace several fields with don't use with other data. */ 1950f66f451Sopenharmony_ci 1960f66f451Sopenharmony_cienum { 1970f66f451Sopenharmony_ci SLOT_pid, /*process id*/ SLOT_ppid, // parent process id 1980f66f451Sopenharmony_ci SLOT_pgrp, /*process group*/ SLOT_sid, // session id 1990f66f451Sopenharmony_ci SLOT_ttynr, /*tty the process uses*/ SLOT_ttypgrp, // pgrp of the tty 2000f66f451Sopenharmony_ci SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults 2010f66f451Sopenharmony_ci SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults 2020f66f451Sopenharmony_ci SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies 2030f66f451Sopenharmony_ci SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime 2040f66f451Sopenharmony_ci SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level 2050f66f451Sopenharmony_ci SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count 2060f66f451Sopenharmony_ci SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot 2070f66f451Sopenharmony_ci SLOT_vsize, /*virtual memory size*/ SLOT_rss, // resident set size 2080f66f451Sopenharmony_ci SLOT_rsslim, /*limit in bytes on rss*/ SLOT_startcode, // code segment addr 2090f66f451Sopenharmony_ci SLOT_endcode, /*code segment address*/ SLOT_startstack,// stack address 2100f66f451Sopenharmony_ci SLOT_esp, /*task stack pointer*/ SLOT_eip, // instruction pointer 2110f66f451Sopenharmony_ci SLOT_iobytes, /*All I/O bytes*/ SLOT_diobytes, // disk I/O bytes 2120f66f451Sopenharmony_ci SLOT_utime2, /*relative utime (top)*/ SLOT_uid, // user id 2130f66f451Sopenharmony_ci SLOT_ruid, /*real user id*/ SLOT_gid, // group id 2140f66f451Sopenharmony_ci SLOT_rgid, /*real group id*/ SLOT_exitsig, // sent to parent 2150f66f451Sopenharmony_ci SLOT_taskcpu, /*CPU running on*/ SLOT_rtprio, // realtime priority 2160f66f451Sopenharmony_ci SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time 2170f66f451Sopenharmony_ci SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child 2180f66f451Sopenharmony_ci SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss 2190f66f451Sopenharmony_ci// end of /proc/$PID/stat fields 2200f66f451Sopenharmony_ci SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length 2210f66f451Sopenharmony_ci SLOT_uptime, /*sysinfo.uptime*/ SLOT_totalram, // sysinfo.totalram 2220f66f451Sopenharmony_ci SLOT_vsz, /*Virtual mem Size*/ SLOT_shr, // Shared memory 2230f66f451Sopenharmony_ci SLOT_pcy, /*Android sched pol*/ SLOT_rchar, // All bytes read 2240f66f451Sopenharmony_ci SLOT_wchar, /*All bytes written*/ SLOT_rbytes, // Disk bytes read 2250f66f451Sopenharmony_ci SLOT_wbytes, /*Disk bytes written*/ SLOT_swap, // Swap pages used 2260f66f451Sopenharmony_ci SLOT_bits, /*32 or 64*/ SLOT_tid, // Thread ID 2270f66f451Sopenharmony_ci SLOT_tcount, /*Thread count*/ 2280f66f451Sopenharmony_ci 2290f66f451Sopenharmony_ci SLOT_count /* Size of array */ 2300f66f451Sopenharmony_ci}; 2310f66f451Sopenharmony_ci 2320f66f451Sopenharmony_ci/* In addition to slot[], carevup contains 6 string fields to display 2330f66f451Sopenharmony_ci command name, tty device, selinux label... They're stored one after the 2340f66f451Sopenharmony_ci other in str[] (separated by null terminators), and offset[] contains the 2350f66f451Sopenharmony_ci starting position of each string after the first (which is always 0). */ 2360f66f451Sopenharmony_ci 2370f66f451Sopenharmony_ci// Data layout in toybuf 2380f66f451Sopenharmony_cistruct procpid { 2390f66f451Sopenharmony_ci long long slot[SLOT_count]; // data (see enum above) 2400f66f451Sopenharmony_ci unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0) 2410f66f451Sopenharmony_ci char state; 2420f66f451Sopenharmony_ci char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME 2430f66f451Sopenharmony_ci}; 2440f66f451Sopenharmony_ci 2450f66f451Sopenharmony_ci/* The typos[] array lists all the types understood by "ps -o", I.E all the 2460f66f451Sopenharmony_ci * columns ps and top know how to display. Each entry has: 2470f66f451Sopenharmony_ci * 2480f66f451Sopenharmony_ci * name: the column name, displayed at top and used to select column with -o 2490f66f451Sopenharmony_ci * 2500f66f451Sopenharmony_ci * width: the display width. Fields are padded to this width when displaying 2510f66f451Sopenharmony_ci * to a terminal (negative means right justified). Strings are truncated 2520f66f451Sopenharmony_ci * to fit, numerical fields are padded but not truncated (although 2530f66f451Sopenharmony_ci * the display code reclaims unused padding from later fields to try to 2540f66f451Sopenharmony_ci * get the overflow back). 2550f66f451Sopenharmony_ci * 2560f66f451Sopenharmony_ci * slot: which slot[] out of procpid. Negative means it's a string field. 2570f66f451Sopenharmony_ci * value|XX requests extra display/sort processing. 2580f66f451Sopenharmony_ci * 2590f66f451Sopenharmony_ci * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the 2600f66f451Sopenharmony_ci * first string argument and the prefix is the first argument to TAGGED_ARRAY 2610f66f451Sopenharmony_ci * so in this case "NAME" becomes PS_NAME which is the offset into typos[] 2620f66f451Sopenharmony_ci * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME). 2630f66f451Sopenharmony_ci * We record active columns in TT.bits, ala: 2640f66f451Sopenharmony_ci * 2650f66f451Sopenharmony_ci * if (TT.bits & _PS_NAME) printf("-o included PS_NAME"); 2660f66f451Sopenharmony_ci */ 2670f66f451Sopenharmony_ci 2680f66f451Sopenharmony_ci#define XX 64 // force string representation for sorting, etc 2690f66f451Sopenharmony_ci 2700f66f451Sopenharmony_ci// TODO: Android uses -30 for LABEL, but ideally it would auto-size. 2710f66f451Sopenharmony_cistruct typography { 2720f66f451Sopenharmony_ci char *name, *help; 2730f66f451Sopenharmony_ci signed char width, slot; 2740f66f451Sopenharmony_ci} static const typos[] = TAGGED_ARRAY(PS, 2750f66f451Sopenharmony_ci // Numbers. (What's in slot[] is what's displayed, sorted numerically.) 2760f66f451Sopenharmony_ci {"PID", "Process ID", 5, SLOT_pid}, 2770f66f451Sopenharmony_ci {"PPID", "Parent Process ID", 5, SLOT_ppid}, 2780f66f451Sopenharmony_ci {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority}, 2790f66f451Sopenharmony_ci {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice}, 2800f66f451Sopenharmony_ci {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip}, 2810f66f451Sopenharmony_ci {"SZ", "4k pages to swap out", 5, SLOT_vsize}, 2820f66f451Sopenharmony_ci {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss}, 2830f66f451Sopenharmony_ci {"PGID", "Process Group ID", 5, SLOT_pgrp}, 2840f66f451Sopenharmony_ci {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize}, 2850f66f451Sopenharmony_ci {"MAJFL", "Major page faults", 6, SLOT_majflt}, 2860f66f451Sopenharmony_ci {"MINFL", "Minor page faults", 6, SLOT_minflt}, 2870f66f451Sopenharmony_ci {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority}, 2880f66f451Sopenharmony_ci {"PSR", "Processor last executed on", 3, SLOT_taskcpu}, 2890f66f451Sopenharmony_ci {"RTPRIO", "Realtime priority", 6, SLOT_rtprio}, 2900f66f451Sopenharmony_ci {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)", 2910f66f451Sopenharmony_ci 3, SLOT_policy}, 2920f66f451Sopenharmony_ci {"CPU", "Which processor running on", 3, SLOT_taskcpu}, 2930f66f451Sopenharmony_ci {"TID", "Thread ID", 5, SLOT_tid}, 2940f66f451Sopenharmony_ci {"TCNT", "Thread count", 4, SLOT_tcount}, 2950f66f451Sopenharmony_ci {"BIT", "32 or 64", 3, SLOT_bits}, 2960f66f451Sopenharmony_ci 2970f66f451Sopenharmony_ci // String fields (-1 is procpid->str, rest are str+offset[1-slot]) 2980f66f451Sopenharmony_ci {"TTY", "Controlling terminal", -8, -2}, 2990f66f451Sopenharmony_ci {"WCHAN", "Wait location in kernel", -6, -3}, 3000f66f451Sopenharmony_ci {"LABEL", "Security label", -30, -4}, 3010f66f451Sopenharmony_ci {"COMM", "EXE filename (/proc/PID/exe)", -27, -5}, 3020f66f451Sopenharmony_ci {"NAME", "Process name (PID's argv[0])", -27, -7}, 3030f66f451Sopenharmony_ci {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5}, 3040f66f451Sopenharmony_ci {"CMDLINE", "Command line (argv[])", -27, -6}, 3050f66f451Sopenharmony_ci {"ARGS", "CMDLINE minus initial path", -27, -6}, 3060f66f451Sopenharmony_ci {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1}, 3070f66f451Sopenharmony_ci 3080f66f451Sopenharmony_ci // user/group (may call getpwuid() or similar) 3090f66f451Sopenharmony_ci {"UID", "User id", 5, SLOT_uid}, 3100f66f451Sopenharmony_ci {"USER", "User name", -12, XX|SLOT_uid}, 3110f66f451Sopenharmony_ci {"RUID", "Real (before suid) user ID", 4, SLOT_ruid}, 3120f66f451Sopenharmony_ci {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid}, 3130f66f451Sopenharmony_ci {"GID", "Group ID", 8, SLOT_gid}, 3140f66f451Sopenharmony_ci {"GROUP", "Group name", -8, XX|SLOT_gid}, 3150f66f451Sopenharmony_ci {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid}, 3160f66f451Sopenharmony_ci {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid}, 3170f66f451Sopenharmony_ci 3180f66f451Sopenharmony_ci // clock displays (00:00:00) 3190f66f451Sopenharmony_ci {"TIME", "CPU time consumed", 8, SLOT_utime}, 3200f66f451Sopenharmony_ci {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime}, 3210f66f451Sopenharmony_ci {"TIME+", "CPU time (high precision)", 9, SLOT_utime}, 3220f66f451Sopenharmony_ci 3230f66f451Sopenharmony_ci // Percentage displays (fixed point, one decimal digit. 123 -> 12.3) 3240f66f451Sopenharmony_ci {"C", "Total %CPU used since start", 1, SLOT_utime2}, 3250f66f451Sopenharmony_ci {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize}, 3260f66f451Sopenharmony_ci {"%MEM", "RSS as % of physical memory", 5, SLOT_rss}, 3270f66f451Sopenharmony_ci {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2}, 3280f66f451Sopenharmony_ci 3290f66f451Sopenharmony_ci // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc) 3300f66f451Sopenharmony_ci {"VIRT", "Virtual memory size", 4, SLOT_vsz}, 3310f66f451Sopenharmony_ci {"RES", "Short RSS", 4, SLOT_rss}, 3320f66f451Sopenharmony_ci {"SHR", "Shared memory", 4, SLOT_shr}, 3330f66f451Sopenharmony_ci {"READ", "Data read", 6, SLOT_rchar}, 3340f66f451Sopenharmony_ci {"WRITE", "Data written", 6, SLOT_wchar}, 3350f66f451Sopenharmony_ci {"IO", "Data I/O", 6, SLOT_iobytes}, 3360f66f451Sopenharmony_ci {"DREAD", "Data read from disk", 6, SLOT_rbytes}, 3370f66f451Sopenharmony_ci {"DWRITE", "Data written to disk", 6, SLOT_wbytes}, 3380f66f451Sopenharmony_ci {"SWAP", "Swap I/O", 6, SLOT_swap}, 3390f66f451Sopenharmony_ci {"DIO", "Disk I/O", 6, SLOT_diobytes}, 3400f66f451Sopenharmony_ci 3410f66f451Sopenharmony_ci // Misc (special cases) 3420f66f451Sopenharmony_ci {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime}, 3430f66f451Sopenharmony_ci {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags}, 3440f66f451Sopenharmony_ci {"S", "Process state:\n" 3450f66f451Sopenharmony_ci "\t R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)\n" 3460f66f451Sopenharmony_ci "\t X (dead) Z (zombie) P (parked) I (idle)\n" 3470f66f451Sopenharmony_ci "\t Also between Linux 2.6.33 and 3.13:\n" 3480f66f451Sopenharmony_ci "\t x (dead) K (wakekill) W (waking)\n", 3490f66f451Sopenharmony_ci -1, XX}, 3500f66f451Sopenharmony_ci {"STAT", "Process state (S) plus:\n" 3510f66f451Sopenharmony_ci "\t < high priority N low priority L locked memory\n" 3520f66f451Sopenharmony_ci "\t s session leader + foreground l multithreaded", 3530f66f451Sopenharmony_ci -5, XX}, 3540f66f451Sopenharmony_ci {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy}, 3550f66f451Sopenharmony_ci); 3560f66f451Sopenharmony_ci 3570f66f451Sopenharmony_ci// Show sorted "-o help" text for fields listed in toybuf[len] 3580f66f451Sopenharmony_cistatic void help_fields(int len, int multi) 3590f66f451Sopenharmony_ci{ 3600f66f451Sopenharmony_ci int i, j, k, left = 0; 3610f66f451Sopenharmony_ci struct typography *t; 3620f66f451Sopenharmony_ci 3630f66f451Sopenharmony_ci // Quick and dirty sort of toybuf[] entries (see TODO below) 3640f66f451Sopenharmony_ci for (j = len; j--; ) { 3650f66f451Sopenharmony_ci k = -1; 3660f66f451Sopenharmony_ci 3670f66f451Sopenharmony_ci for (i=0; i<j; i++) { 3680f66f451Sopenharmony_ci if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) { 3690f66f451Sopenharmony_ci k = toybuf[i]; 3700f66f451Sopenharmony_ci toybuf[i] = toybuf[i+1]; 3710f66f451Sopenharmony_ci toybuf[i+1] = k; 3720f66f451Sopenharmony_ci } 3730f66f451Sopenharmony_ci } 3740f66f451Sopenharmony_ci if (k == -1) break; 3750f66f451Sopenharmony_ci } 3760f66f451Sopenharmony_ci 3770f66f451Sopenharmony_ci // Display loop 3780f66f451Sopenharmony_ci for (i = j = 0; i<len; i++, j++) { 3790f66f451Sopenharmony_ci t = (void *)(typos+toybuf[i]); 3800f66f451Sopenharmony_ci if (strlen(t->help)>30) { 3810f66f451Sopenharmony_ci if (multi) printf(" %-8s%s\n", t->name, t->help); 3820f66f451Sopenharmony_ci else j--; 3830f66f451Sopenharmony_ci } else if (!multi) { 3840f66f451Sopenharmony_ci left = !(j&1); 3850f66f451Sopenharmony_ci printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left); 3860f66f451Sopenharmony_ci } 3870f66f451Sopenharmony_ci } 3880f66f451Sopenharmony_ci if (!multi && left) xputc('\n'); 3890f66f451Sopenharmony_ci} 3900f66f451Sopenharmony_ci 3910f66f451Sopenharmony_ci// Print help text for each -o field, with categories. 3920f66f451Sopenharmony_cistatic void help_help(void) 3930f66f451Sopenharmony_ci{ 3940f66f451Sopenharmony_ci int i, jump = PS_CMD+1-PS_COMM; 3950f66f451Sopenharmony_ci 3960f66f451Sopenharmony_ci // TODO: sort the array of -o types so they're already alphabetical and 3970f66f451Sopenharmony_ci // don't need sorting here. A regex to find everything that currently cares 3980f66f451Sopenharmony_ci // about symbol order might be: "which *[><]=* *PS" 3990f66f451Sopenharmony_ci 4000f66f451Sopenharmony_ci // First show the half-dozen variants of command line display. 4010f66f451Sopenharmony_ci 4020f66f451Sopenharmony_ci printf("Command line field types:\n\n"); 4030f66f451Sopenharmony_ci for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i; 4040f66f451Sopenharmony_ci help_fields(jump, 0); 4050f66f451Sopenharmony_ci 4060f66f451Sopenharmony_ci // Show the rest of the -o types, starting with the ones that don't columnize 4070f66f451Sopenharmony_ci 4080f66f451Sopenharmony_ci printf("\nProcess attribute field types:\n\n"); 4090f66f451Sopenharmony_ci for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump; 4100f66f451Sopenharmony_ci help_fields(ARRAY_LEN(typos)-jump, 1); 4110f66f451Sopenharmony_ci help_fields(ARRAY_LEN(typos)-jump, 0); 4120f66f451Sopenharmony_ci 4130f66f451Sopenharmony_ci xexit(); 4140f66f451Sopenharmony_ci} 4150f66f451Sopenharmony_ci 4160f66f451Sopenharmony_ci// process match filter for top/ps/pgrep: Return 0 to discard, nonzero to keep 4170f66f451Sopenharmony_cistatic int shared_match_process(long long *slot) 4180f66f451Sopenharmony_ci{ 4190f66f451Sopenharmony_ci struct ptr_len match[] = { 4200f66f451Sopenharmony_ci {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid}, 4210f66f451Sopenharmony_ci {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr}, 4220f66f451Sopenharmony_ci {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid} 4230f66f451Sopenharmony_ci }; 4240f66f451Sopenharmony_ci int i, j; 4250f66f451Sopenharmony_ci long *ll = 0; 4260f66f451Sopenharmony_ci 4270f66f451Sopenharmony_ci // Do we have -g -G -p -P -s -t -u -U options selecting processes? 4280f66f451Sopenharmony_ci for (i = 0; i < ARRAY_LEN(match); i++) { 4290f66f451Sopenharmony_ci struct ptr_len *mm = match[i].ptr; 4300f66f451Sopenharmony_ci 4310f66f451Sopenharmony_ci if (mm->len) { 4320f66f451Sopenharmony_ci ll = mm->ptr; 4330f66f451Sopenharmony_ci for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1; 4340f66f451Sopenharmony_ci } 4350f66f451Sopenharmony_ci } 4360f66f451Sopenharmony_ci 4370f66f451Sopenharmony_ci return ll ? 0 : -1; 4380f66f451Sopenharmony_ci} 4390f66f451Sopenharmony_ci 4400f66f451Sopenharmony_ci// process match filter for ps: Return 0 to discard, nonzero to keep 4410f66f451Sopenharmony_cistatic int ps_match_process(long long *slot) 4420f66f451Sopenharmony_ci{ 4430f66f451Sopenharmony_ci int i = shared_match_process(slot); 4440f66f451Sopenharmony_ci 4450f66f451Sopenharmony_ci if (i>0) return 1; 4460f66f451Sopenharmony_ci // If we had selections and didn't match them, don't display 4470f66f451Sopenharmony_ci if (!i) return 0; 4480f66f451Sopenharmony_ci 4490f66f451Sopenharmony_ci // Filter implicit categories for other display types 4500f66f451Sopenharmony_ci if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0; 4510f66f451Sopenharmony_ci if (FLAG(a) && !slot[SLOT_ttynr]) return 0; 4520f66f451Sopenharmony_ci if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr]) 4530f66f451Sopenharmony_ci return 0; 4540f66f451Sopenharmony_ci 4550f66f451Sopenharmony_ci return 1; 4560f66f451Sopenharmony_ci} 4570f66f451Sopenharmony_ci 4580f66f451Sopenharmony_ci// Generate display string (260 bytes at end of toybuf) from struct ofield 4590f66f451Sopenharmony_cistatic char *string_field(struct procpid *tb, struct ofields *field) 4600f66f451Sopenharmony_ci{ 4610f66f451Sopenharmony_ci char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s; 4620f66f451Sopenharmony_ci int which = field->which, sl = typos[which].slot; 4630f66f451Sopenharmony_ci long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0; 4640f66f451Sopenharmony_ci 4650f66f451Sopenharmony_ci // numbers, mostly from /proc/$PID/stat 4660f66f451Sopenharmony_ci if (which <= PS_BIT) { 4670f66f451Sopenharmony_ci char *fmt = "%lld"; 4680f66f451Sopenharmony_ci 4690f66f451Sopenharmony_ci if (which==PS_PRI) ll = 39-ll; 4700f66f451Sopenharmony_ci if (which==PS_ADDR) fmt = "%llx"; 4710f66f451Sopenharmony_ci else if (which==PS_SZ) ll >>= 12; 4720f66f451Sopenharmony_ci else if (which==PS_RSS) ll <<= 2; 4730f66f451Sopenharmony_ci else if (which==PS_VSZ) ll >>= 10; 4740f66f451Sopenharmony_ci else if (which==PS_PR && ll<-9) fmt="RT"; 4750f66f451Sopenharmony_ci else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-"; 4760f66f451Sopenharmony_ci sprintf(out, fmt, ll); 4770f66f451Sopenharmony_ci 4780f66f451Sopenharmony_ci // String fields 4790f66f451Sopenharmony_ci } else if (sl < 0) { 4800f66f451Sopenharmony_ci out = tb->str; 4810f66f451Sopenharmony_ci sl *= -1; 4820f66f451Sopenharmony_ci // First string slot has offset 0, others are offset[-slot-2] 4830f66f451Sopenharmony_ci if (--sl) out += tb->offset[--sl]; 4840f66f451Sopenharmony_ci if (which==PS_ARGS || which==PS_COMM) { 4850f66f451Sopenharmony_ci int i; 4860f66f451Sopenharmony_ci 4870f66f451Sopenharmony_ci s = out; 4880f66f451Sopenharmony_ci for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++) 4890f66f451Sopenharmony_ci if (out[i] == '/') s = out+i+1; 4900f66f451Sopenharmony_ci out = s; 4910f66f451Sopenharmony_ci } 4920f66f451Sopenharmony_ci if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str); 4930f66f451Sopenharmony_ci 4940f66f451Sopenharmony_ci // user/group 4950f66f451Sopenharmony_ci } else if (which <= PS_RGROUP) { 4960f66f451Sopenharmony_ci sprintf(out, "%lld", ll); 4970f66f451Sopenharmony_ci if (sl&XX) { 4980f66f451Sopenharmony_ci if (which > PS_RUSER) { 4990f66f451Sopenharmony_ci struct group *gr = bufgetgrgid(ll); 5000f66f451Sopenharmony_ci 5010f66f451Sopenharmony_ci if (gr) out = gr->gr_name; 5020f66f451Sopenharmony_ci } else { 5030f66f451Sopenharmony_ci struct passwd *pw = bufgetpwuid(ll); 5040f66f451Sopenharmony_ci 5050f66f451Sopenharmony_ci if (pw) out = pw->pw_name; 5060f66f451Sopenharmony_ci } 5070f66f451Sopenharmony_ci } 5080f66f451Sopenharmony_ci 5090f66f451Sopenharmony_ci // Clock displays 5100f66f451Sopenharmony_ci } else if (which <= PS_TIME_) { 5110f66f451Sopenharmony_ci int unit = 60, pad = 2, j = TT.ticks; 5120f66f451Sopenharmony_ci time_t seconds; 5130f66f451Sopenharmony_ci 5140f66f451Sopenharmony_ci if (which!=PS_TIME_) unit *= 60*24; 5150f66f451Sopenharmony_ci else pad = 0; 5160f66f451Sopenharmony_ci // top adjusts slot[SLOT_upticks], we want original meaning. 5170f66f451Sopenharmony_ci if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime]; 5180f66f451Sopenharmony_ci seconds = ll/j; 5190f66f451Sopenharmony_ci 5200f66f451Sopenharmony_ci // Output days-hours:mins:secs, skipping non-required fields with zero 5210f66f451Sopenharmony_ci // TIME has 3 required fields, ETIME has 2. (Posix!) TIME+ is from top 5220f66f451Sopenharmony_ci for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) { 5230f66f451Sopenharmony_ci if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out; 5240f66f451Sopenharmony_ci if (s) { 5250f66f451Sopenharmony_ci s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit)); 5260f66f451Sopenharmony_ci pad = 2; 5270f66f451Sopenharmony_ci if ((*s = "-::"[j])) s++; 5280f66f451Sopenharmony_ci } 5290f66f451Sopenharmony_ci seconds %= unit; 5300f66f451Sopenharmony_ci unit /= j ? 60 : 24; 5310f66f451Sopenharmony_ci } 5320f66f451Sopenharmony_ci if (which==PS_TIME_ && s-out<8) 5330f66f451Sopenharmony_ci sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks); 5340f66f451Sopenharmony_ci 5350f66f451Sopenharmony_ci // Percentage displays 5360f66f451Sopenharmony_ci } else if (which <= PS__CPU) { 5370f66f451Sopenharmony_ci ll = slot[sl&(XX-1)]*1000; 5380f66f451Sopenharmony_ci if (which==PS__VSZ || which==PS__MEM) 5390f66f451Sopenharmony_ci ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096); 5400f66f451Sopenharmony_ci else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks]; 5410f66f451Sopenharmony_ci sl = ll; 5420f66f451Sopenharmony_ci if (which==PS_C) sl += 5; 5430f66f451Sopenharmony_ci sprintf(out, "%d", sl/10); 5440f66f451Sopenharmony_ci if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10); 5450f66f451Sopenharmony_ci 5460f66f451Sopenharmony_ci // Human readable 5470f66f451Sopenharmony_ci } else if (which <= PS_DIO) { 5480f66f451Sopenharmony_ci int i = abs(field->len); 5490f66f451Sopenharmony_ci 5500f66f451Sopenharmony_ci if (i<4) i = 4; 5510f66f451Sopenharmony_ci s = out; 5520f66f451Sopenharmony_ci if ((ll = slot[typos[which].slot])<0) { 5530f66f451Sopenharmony_ci ll = -ll; 5540f66f451Sopenharmony_ci *s++ = '-'; 5550f66f451Sopenharmony_ci if (i>4) i--; 5560f66f451Sopenharmony_ci } 5570f66f451Sopenharmony_ci if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE); 5580f66f451Sopenharmony_ci if (TT.forcek) sprintf(out, "%lldk", ll/1024); 5590f66f451Sopenharmony_ci else human_readable_long(s, ll, i-1, 0); 5600f66f451Sopenharmony_ci 5610f66f451Sopenharmony_ci // Posix doesn't specify what flags should say. Man page says 5620f66f451Sopenharmony_ci // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h 5630f66f451Sopenharmony_ci } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5); 5640f66f451Sopenharmony_ci else if (which==PS_S || which==PS_STAT) { 5650f66f451Sopenharmony_ci s = out; 5660f66f451Sopenharmony_ci *s++ = tb->state; 5670f66f451Sopenharmony_ci if (which==PS_STAT) { 5680f66f451Sopenharmony_ci // TODO l = multithreaded 5690f66f451Sopenharmony_ci if (slot[SLOT_nice]<0) *s++ = '<'; 5700f66f451Sopenharmony_ci else if (slot[SLOT_nice]>0) *s++ = 'N'; 5710f66f451Sopenharmony_ci if (slot[SLOT_sid]==*slot) *s++ = 's'; 5720f66f451Sopenharmony_ci if (slot[SLOT_vmlck]) *s++ = 'L'; 5730f66f451Sopenharmony_ci if (slot[SLOT_ttypgrp]==*slot) *s++ = '+'; 5740f66f451Sopenharmony_ci } 5750f66f451Sopenharmony_ci *s = 0; 5760f66f451Sopenharmony_ci } else if (which==PS_STIME) { 5770f66f451Sopenharmony_ci time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks; 5780f66f451Sopenharmony_ci 5790f66f451Sopenharmony_ci // Padding behavior's a bit odd: default field size is just hh:mm. 5800f66f451Sopenharmony_ci // Increasing stime:size reveals more data at left until full, 5810f66f451Sopenharmony_ci // so move start address so yyyy-mm-dd hh:mm revealed on left at :16, 5820f66f451Sopenharmony_ci // then add :ss on right for :19. 5830f66f451Sopenharmony_ci strftime(out, 260, "%F %T", localtime(&t)); 5840f66f451Sopenharmony_ci out = out+strlen(out)-3-abs(field->len); 5850f66f451Sopenharmony_ci if (out<buf) out = buf; 5860f66f451Sopenharmony_ci 5870f66f451Sopenharmony_ci } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll)); 5880f66f451Sopenharmony_ci else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which); 5890f66f451Sopenharmony_ci 5900f66f451Sopenharmony_ci return out; 5910f66f451Sopenharmony_ci} 5920f66f451Sopenharmony_ci 5930f66f451Sopenharmony_ci// Display process data that get_ps() read from /proc, formatting via TT.fields 5940f66f451Sopenharmony_cistatic void show_ps(void *p) 5950f66f451Sopenharmony_ci{ 5960f66f451Sopenharmony_ci struct procpid *tb = p; 5970f66f451Sopenharmony_ci struct ofields *field; 5980f66f451Sopenharmony_ci int pad, len, width = TT.width, abslen, sign, olen, extra = 0; 5990f66f451Sopenharmony_ci 6000f66f451Sopenharmony_ci // Loop through fields to display 6010f66f451Sopenharmony_ci for (field = TT.fields; field; field = field->next) { 6020f66f451Sopenharmony_ci char *out = string_field(tb, field); 6030f66f451Sopenharmony_ci 6040f66f451Sopenharmony_ci // Output the field, appropriately padded 6050f66f451Sopenharmony_ci 6060f66f451Sopenharmony_ci // Minimum one space between each field 6070f66f451Sopenharmony_ci if (width<2) break; 6080f66f451Sopenharmony_ci if (field != TT.fields) { 6090f66f451Sopenharmony_ci putchar(' '); 6100f66f451Sopenharmony_ci width--; 6110f66f451Sopenharmony_ci } 6120f66f451Sopenharmony_ci 6130f66f451Sopenharmony_ci // Don't truncate number fields, but try to reclaim extra offset from later 6140f66f451Sopenharmony_ci // fields that can naturally be shorter 6150f66f451Sopenharmony_ci abslen = abs(field->len); 6160f66f451Sopenharmony_ci sign = field->len<0 ? -1 : 1; 6170f66f451Sopenharmony_ci olen = (TT.tty) ? utf8len(out) : strlen(out); 6180f66f451Sopenharmony_ci if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) { 6190f66f451Sopenharmony_ci // overflow but remember by how much 6200f66f451Sopenharmony_ci extra += olen-abslen; 6210f66f451Sopenharmony_ci abslen = olen; 6220f66f451Sopenharmony_ci } else if (extra && olen<abslen) { 6230f66f451Sopenharmony_ci int unused = abslen-olen; 6240f66f451Sopenharmony_ci 6250f66f451Sopenharmony_ci // If later fields have slack space, take back overflow 6260f66f451Sopenharmony_ci if (unused>extra) unused = extra; 6270f66f451Sopenharmony_ci abslen -= unused; 6280f66f451Sopenharmony_ci extra -= unused; 6290f66f451Sopenharmony_ci } 6300f66f451Sopenharmony_ci if (abslen>width) abslen = width; 6310f66f451Sopenharmony_ci len = pad = abslen; 6320f66f451Sopenharmony_ci pad *= sign; 6330f66f451Sopenharmony_ci 6340f66f451Sopenharmony_ci // If last field is left justified, no trailing spaces. 6350f66f451Sopenharmony_ci if (!field->next && sign<0) { 6360f66f451Sopenharmony_ci pad = -1; 6370f66f451Sopenharmony_ci len = width; 6380f66f451Sopenharmony_ci } 6390f66f451Sopenharmony_ci 6400f66f451Sopenharmony_ci // If we truncated a left-justified field, show + instead of last char 6410f66f451Sopenharmony_ci if (olen>len && len>1 && sign<0) { 6420f66f451Sopenharmony_ci width--; 6430f66f451Sopenharmony_ci len--; 6440f66f451Sopenharmony_ci if (field->next) pad++; 6450f66f451Sopenharmony_ci abslen = 0; 6460f66f451Sopenharmony_ci } 6470f66f451Sopenharmony_ci 6480f66f451Sopenharmony_ci if (TT.tty) width -= draw_trim(out, pad, len); 6490f66f451Sopenharmony_ci else width -= printf("%*.*s", pad, len, out); 6500f66f451Sopenharmony_ci if (!abslen) putchar('+'); 6510f66f451Sopenharmony_ci if (!width) break; 6520f66f451Sopenharmony_ci } 6530f66f451Sopenharmony_ci putchar(TT.time ? '\r' : '\n'); 6540f66f451Sopenharmony_ci} 6550f66f451Sopenharmony_ci 6560f66f451Sopenharmony_ci// dirtree callback: read data about a process, then display or store it. 6570f66f451Sopenharmony_ci// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra 6580f66f451Sopenharmony_ci// (in -k mode) or calls show_ps directly on toybuf (for low memory systems). 6590f66f451Sopenharmony_cistatic int get_ps(struct dirtree *new) 6600f66f451Sopenharmony_ci{ 6610f66f451Sopenharmony_ci struct { 6620f66f451Sopenharmony_ci char *name; // Path under /proc/$PID directory 6630f66f451Sopenharmony_ci long long bits; // Only fetch extra data if an -o field is displaying it 6640f66f451Sopenharmony_ci } fetch[] = { 6650f66f451Sopenharmony_ci // sources for procpid->offset[] data 6660f66f451Sopenharmony_ci {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL}, 6670f66f451Sopenharmony_ci {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}, 6680f66f451Sopenharmony_ci {"", _PS_NAME} 6690f66f451Sopenharmony_ci }; 6700f66f451Sopenharmony_ci struct procpid *tb = (void *)toybuf; 6710f66f451Sopenharmony_ci long long *slot = tb->slot; 6720f66f451Sopenharmony_ci char *name, *s, *buf = tb->str, *end = 0; 6730f66f451Sopenharmony_ci struct sysinfo si; 6740f66f451Sopenharmony_ci int i, j, fd; 6750f66f451Sopenharmony_ci off_t len; 6760f66f451Sopenharmony_ci 6770f66f451Sopenharmony_ci // Recurse one level into /proc children, skip non-numeric entries 6780f66f451Sopenharmony_ci if (!new->parent) 6790f66f451Sopenharmony_ci return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC 6800f66f451Sopenharmony_ci |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process)); 6810f66f451Sopenharmony_ci 6820f66f451Sopenharmony_ci // Grab PID and figure out if we're a thread or a process 6830f66f451Sopenharmony_ci memset(slot, 0, sizeof(tb->slot)); 6840f66f451Sopenharmony_ci slot[SLOT_tid] = *slot = atol(new->name); 6850f66f451Sopenharmony_ci if (TT.threadparent && TT.threadparent->extra) { 6860f66f451Sopenharmony_ci struct procpid *tb2 = (struct procpid *)TT.threadparent->extra; 6870f66f451Sopenharmony_ci 6880f66f451Sopenharmony_ci *slot = *tb2->slot; 6890f66f451Sopenharmony_ci // Parent also shows up as a thread, but we need to reread task/stat fields 6900f66f451Sopenharmony_ci // to get non-collated info for just parent thread (vs whole process). 6910f66f451Sopenharmony_ci if (*slot == slot[SLOT_tid]) slot = tb2->slot; 6920f66f451Sopenharmony_ci } 6930f66f451Sopenharmony_ci fd = dirtree_parentfd(new); 6940f66f451Sopenharmony_ci 6950f66f451Sopenharmony_ci // Read /proc/$PID/stat into half of toybuf. 6960f66f451Sopenharmony_ci len = 2048; 6970f66f451Sopenharmony_ci sprintf(buf, "%lld/stat", slot[SLOT_tid]); 6980f66f451Sopenharmony_ci if (!readfileat(fd, buf, buf, &len)) return 0; 6990f66f451Sopenharmony_ci 7000f66f451Sopenharmony_ci // parse oddball fields: the first field is same as new->name (skip it) 7010f66f451Sopenharmony_ci // and the second and third (name and state) are the only non-numeric fields. 7020f66f451Sopenharmony_ci // Name has (parentheses) around it, and can have embedded ')' so match 7030f66f451Sopenharmony_ci // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that). 7040f66f451Sopenharmony_ci // TODO: kernel task struct actually limits name to 16 chars? 7050f66f451Sopenharmony_ci if (!(name = strchr(buf, '('))) return 0; 7060f66f451Sopenharmony_ci for (s = ++name; *s; s++) if (*s == ')') end = s; 7070f66f451Sopenharmony_ci if (!end || end-name>255) return 0; 7080f66f451Sopenharmony_ci if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0; 7090f66f451Sopenharmony_ci 7100f66f451Sopenharmony_ci // All remaining fields should be numeric, parse them into slot[] array 7110f66f451Sopenharmony_ci // (skipping first 3 stat fields and first slot[], both were handled above) 7120f66f451Sopenharmony_ci // yes this means the alignment's off: stat[4] becomes slot[1] 7130f66f451Sopenharmony_ci for (j = SLOT_ppid; j<SLOT_upticks; j++) 7140f66f451Sopenharmony_ci if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break; 7150f66f451Sopenharmony_ci 7160f66f451Sopenharmony_ci // Now we've read the data, move status and name right after slot[] array, 7170f66f451Sopenharmony_ci // and convert low chars to ? for non-tty display while we're at it. 7180f66f451Sopenharmony_ci for (i = 0; i<end-name; i++) 7190f66f451Sopenharmony_ci if ((tb->str[i] = name[i]) < ' ') 7200f66f451Sopenharmony_ci if (!TT.tty) tb->str[i] = '?'; 7210f66f451Sopenharmony_ci buf = tb->str+i; 7220f66f451Sopenharmony_ci *buf++ = 0; 7230f66f451Sopenharmony_ci len = sizeof(toybuf)-(buf-toybuf); 7240f66f451Sopenharmony_ci 7250f66f451Sopenharmony_ci // Overwrite useless/obsolete stat fields with more interesting data. 7260f66f451Sopenharmony_ci 7270f66f451Sopenharmony_ci // save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch 7280f66f451Sopenharmony_ci // or numeric wchan, and the remaining two are always zero), and vmlck into 7290f66f451Sopenharmony_ci // 18 (which is "obsolete, always 0" from stat) 7300f66f451Sopenharmony_ci slot[SLOT_uid] = new->st.st_uid; 7310f66f451Sopenharmony_ci slot[SLOT_gid] = new->st.st_gid; 7320f66f451Sopenharmony_ci 7330f66f451Sopenharmony_ci // TIME and TIME+ use combined value, ksort needs 'em added. 7340f66f451Sopenharmony_ci slot[SLOT_utime] += slot[SLOT_stime]; 7350f66f451Sopenharmony_ci slot[SLOT_utime2] = slot[SLOT_utime]; 7360f66f451Sopenharmony_ci 7370f66f451Sopenharmony_ci // Do we need to read "status"? 7380f66f451Sopenharmony_ci if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP 7390f66f451Sopenharmony_ci |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len) 7400f66f451Sopenharmony_ci { 7410f66f451Sopenharmony_ci off_t temp = len; 7420f66f451Sopenharmony_ci 7430f66f451Sopenharmony_ci sprintf(buf, "%lld/status", slot[SLOT_tid]); 7440f66f451Sopenharmony_ci if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 7450f66f451Sopenharmony_ci s = strafter(buf, "\nUid:"); 7460f66f451Sopenharmony_ci slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid; 7470f66f451Sopenharmony_ci s = strafter(buf, "\nGid:"); 7480f66f451Sopenharmony_ci slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid; 7490f66f451Sopenharmony_ci if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024; 7500f66f451Sopenharmony_ci if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024; 7510f66f451Sopenharmony_ci } 7520f66f451Sopenharmony_ci 7530f66f451Sopenharmony_ci // Do we need to read "io"? 7540f66f451Sopenharmony_ci if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) { 7550f66f451Sopenharmony_ci off_t temp = len; 7560f66f451Sopenharmony_ci 7570f66f451Sopenharmony_ci sprintf(buf, "%lld/io", slot[SLOT_tid]); 7580f66f451Sopenharmony_ci if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 7590f66f451Sopenharmony_ci if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s); 7600f66f451Sopenharmony_ci if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s); 7610f66f451Sopenharmony_ci if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s); 7620f66f451Sopenharmony_ci if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s); 7630f66f451Sopenharmony_ci slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap]; 7640f66f451Sopenharmony_ci slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap]; 7650f66f451Sopenharmony_ci } 7660f66f451Sopenharmony_ci 7670f66f451Sopenharmony_ci // If we were updating thread parent with its own task info, we're done. 7680f66f451Sopenharmony_ci if (slot != tb->slot) return 0; 7690f66f451Sopenharmony_ci 7700f66f451Sopenharmony_ci // We now know enough to skip processes we don't care about. 7710f66f451Sopenharmony_ci if (TT.match_process && !TT.match_process(slot)) return 0; 7720f66f451Sopenharmony_ci 7730f66f451Sopenharmony_ci // /proc data is generated as it's read, so for maximum accuracy on slow 7740f66f451Sopenharmony_ci // systems (or ps | more) we re-fetch uptime as we fetch each /proc line. 7750f66f451Sopenharmony_ci sysinfo(&si); 7760f66f451Sopenharmony_ci slot[SLOT_uptime] = si.uptime; 7770f66f451Sopenharmony_ci slot[SLOT_totalram] = si.totalram; 7780f66f451Sopenharmony_ci slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime]; 7790f66f451Sopenharmony_ci 7800f66f451Sopenharmony_ci // Do we need to read "statm"? 7810f66f451Sopenharmony_ci if (TT.bits&(_PS_VIRT|_PS_SHR)) { 7820f66f451Sopenharmony_ci off_t temp = len; 7830f66f451Sopenharmony_ci 7840f66f451Sopenharmony_ci sprintf(buf, "%lld/statm", slot[SLOT_tid]); 7850f66f451Sopenharmony_ci if (!readfileat(fd, buf, buf, &temp)) *buf = 0; 7860f66f451Sopenharmony_ci 7870f66f451Sopenharmony_ci // Skip redundant RSS field, we got it from stat. 7880f66f451Sopenharmony_ci slot[SLOT_vsz] = slot[SLOT_shr] = 0; 7890f66f451Sopenharmony_ci sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]); 7900f66f451Sopenharmony_ci } 7910f66f451Sopenharmony_ci 7920f66f451Sopenharmony_ci // Do we need to read "exe"? 7930f66f451Sopenharmony_ci if (TT.bits&_PS_BIT) { 7940f66f451Sopenharmony_ci off_t temp = 6; 7950f66f451Sopenharmony_ci 7960f66f451Sopenharmony_ci sprintf(buf, "%lld/exe", slot[SLOT_tid]); 7970f66f451Sopenharmony_ci if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) { 7980f66f451Sopenharmony_ci if (buf[4] == 1) slot[SLOT_bits] = 32; 7990f66f451Sopenharmony_ci else if (buf[4] == 2) slot[SLOT_bits] = 64; 8000f66f451Sopenharmony_ci } 8010f66f451Sopenharmony_ci } 8020f66f451Sopenharmony_ci 8030f66f451Sopenharmony_ci // Do we need Android scheduling policy? 8040f66f451Sopenharmony_ci if (TT.bits&_PS_PCY) 8050f66f451Sopenharmony_ci get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]); 8060f66f451Sopenharmony_ci 8070f66f451Sopenharmony_ci // Done using buf[] (tb->str) as scratch space, now read string data, 8080f66f451Sopenharmony_ci // saving consective null terminated strings. (Save starting offsets into 8090f66f451Sopenharmony_ci // str->offset to avoid strlen() loop to find relevant string.) 8100f66f451Sopenharmony_ci 8110f66f451Sopenharmony_ci // Fetch string data while parentfd still available, appending to buf. 8120f66f451Sopenharmony_ci // (There's well over 3k of toybuf left. We could dynamically malloc, but 8130f66f451Sopenharmony_ci // it'd almost never get used, querying length of a proc file is awkward, 8140f66f451Sopenharmony_ci // fixed buffer is nommu friendly... Wait for somebody to complain. :) 8150f66f451Sopenharmony_ci 8160f66f451Sopenharmony_ci // The fetch[] array at the start of the function says what file to read 8170f66f451Sopenharmony_ci // and what -o display field outputs it (to skip the ones we don't need). 8180f66f451Sopenharmony_ci 8190f66f451Sopenharmony_ci slot[SLOT_argv0len] = 0; 8200f66f451Sopenharmony_ci for (j = 0; j<ARRAY_LEN(fetch); j++) { 8210f66f451Sopenharmony_ci tb->offset[j] = buf-(tb->str); 8220f66f451Sopenharmony_ci if (!(TT.bits&fetch[j].bits)) { 8230f66f451Sopenharmony_ci *buf++ = 0; 8240f66f451Sopenharmony_ci continue; 8250f66f451Sopenharmony_ci } 8260f66f451Sopenharmony_ci 8270f66f451Sopenharmony_ci // Determine available space: reserve 256 bytes (guaranteed minimum) for 8280f66f451Sopenharmony_ci // each string we haven't checked yet, tb->str starts after the numeric 8290f66f451Sopenharmony_ci // arrays in struct procpid, and we reserve 260 bytes scratch space at the 8300f66f451Sopenharmony_ci // end of toybuf for output conversion in string_field(). Other than that, 8310f66f451Sopenharmony_ci // each use all available space, and future strings that don't use their 8320f66f451Sopenharmony_ci // guaranteed minimum add to the pool. 8330f66f451Sopenharmony_ci len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260; 8340f66f451Sopenharmony_ci sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name); 8350f66f451Sopenharmony_ci 8360f66f451Sopenharmony_ci // For exe (j==3) readlink() instead of reading file's contents 8370f66f451Sopenharmony_ci // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID). 8380f66f451Sopenharmony_ci if (j==3 || j==5) { 8390f66f451Sopenharmony_ci struct procpid *ptb = 0; 8400f66f451Sopenharmony_ci int k; 8410f66f451Sopenharmony_ci 8420f66f451Sopenharmony_ci // Thread doesn't have exe or argv[0], so use parent's 8430f66f451Sopenharmony_ci if (TT.threadparent && TT.threadparent->extra) 8440f66f451Sopenharmony_ci ptb = (void *)TT.threadparent->extra; 8450f66f451Sopenharmony_ci 8460f66f451Sopenharmony_ci if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len); 8470f66f451Sopenharmony_ci else { 8480f66f451Sopenharmony_ci if (j==3) i = strlen(s = ptb->str+ptb->offset[3]); 8490f66f451Sopenharmony_ci else { 8500f66f451Sopenharmony_ci if (!ptb || slot[SLOT_argv0len]) ptb = tb; 8510f66f451Sopenharmony_ci i = ptb->slot[SLOT_argv0len]; 8520f66f451Sopenharmony_ci s = ptb->str+ptb->offset[4]; 8530f66f451Sopenharmony_ci while (-1!=(k = stridx(s, '/')) && k<i) { 8540f66f451Sopenharmony_ci s += k+1; 8550f66f451Sopenharmony_ci i -= k+1; 8560f66f451Sopenharmony_ci } 8570f66f451Sopenharmony_ci } 8580f66f451Sopenharmony_ci if (i<len) len = i; 8590f66f451Sopenharmony_ci memcpy(buf, s, len); 8600f66f451Sopenharmony_ci buf[len] = 0; 8610f66f451Sopenharmony_ci } 8620f66f451Sopenharmony_ci 8630f66f451Sopenharmony_ci // Turning stat's SLOT_ttynr into a string is an outright heuristic ordeal. 8640f66f451Sopenharmony_ci } else if (!j) { 8650f66f451Sopenharmony_ci int rdev = slot[SLOT_ttynr]; 8660f66f451Sopenharmony_ci struct stat st; 8670f66f451Sopenharmony_ci 8680f66f451Sopenharmony_ci // Call no tty "?" rather than "0:0". 8690f66f451Sopenharmony_ci strcpy(buf, "?"); 8700f66f451Sopenharmony_ci if (rdev) { 8710f66f451Sopenharmony_ci // Can we readlink() our way to a name? 8720f66f451Sopenharmony_ci for (i = 0; i<3; i++) { 8730f66f451Sopenharmony_ci sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i); 8740f66f451Sopenharmony_ci if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode) 8750f66f451Sopenharmony_ci && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len))) 8760f66f451Sopenharmony_ci break; 8770f66f451Sopenharmony_ci } 8780f66f451Sopenharmony_ci 8790f66f451Sopenharmony_ci // Couldn't find it, try all the tty drivers. 8800f66f451Sopenharmony_ci if (i == 3) { 8810f66f451Sopenharmony_ci FILE *fp = fopen("/proc/tty/drivers", "r"); 8820f66f451Sopenharmony_ci int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev); 8830f66f451Sopenharmony_ci 8840f66f451Sopenharmony_ci if (fp) { 8850f66f451Sopenharmony_ci while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) { 8860f66f451Sopenharmony_ci // TODO: we could parse the minor range too. 8870f66f451Sopenharmony_ci if (tty_major == maj) { 8880f66f451Sopenharmony_ci len = strlen(buf); 8890f66f451Sopenharmony_ci len += sprintf(buf+len, "%d", min); 8900f66f451Sopenharmony_ci if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev) 8910f66f451Sopenharmony_ci break; 8920f66f451Sopenharmony_ci } 8930f66f451Sopenharmony_ci tty_major = 0; 8940f66f451Sopenharmony_ci } 8950f66f451Sopenharmony_ci fclose(fp); 8960f66f451Sopenharmony_ci } 8970f66f451Sopenharmony_ci 8980f66f451Sopenharmony_ci // Really couldn't find it, so just show major:minor. 8990f66f451Sopenharmony_ci if (!tty_major) len = sprintf(buf, "%d:%d", maj, min); 9000f66f451Sopenharmony_ci } 9010f66f451Sopenharmony_ci 9020f66f451Sopenharmony_ci s = buf; 9030f66f451Sopenharmony_ci if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4); 9040f66f451Sopenharmony_ci } 9050f66f451Sopenharmony_ci 9060f66f451Sopenharmony_ci // For the rest, the data we want is in a file we can just read. 9070f66f451Sopenharmony_ci } else { 9080f66f451Sopenharmony_ci int temp = 0; 9090f66f451Sopenharmony_ci 9100f66f451Sopenharmony_ci // When command has no arguments, don't space over the NUL 9110f66f451Sopenharmony_ci if (readfileat(fd, buf, buf, &len) && len>0) { 9120f66f451Sopenharmony_ci 9130f66f451Sopenharmony_ci // Trim trailing whitespace and NUL bytes 9140f66f451Sopenharmony_ci while (len) 9150f66f451Sopenharmony_ci if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0; 9160f66f451Sopenharmony_ci else break; 9170f66f451Sopenharmony_ci 9180f66f451Sopenharmony_ci // Turn NUL to space, other low ascii to ? (in non-tty mode), except 9190f66f451Sopenharmony_ci // cmdline has a trailing NUL that we don't want to turn to space. 9200f66f451Sopenharmony_ci for (i=0; i<len-1; i++) { 9210f66f451Sopenharmony_ci char c = buf[i]; 9220f66f451Sopenharmony_ci 9230f66f451Sopenharmony_ci if (!c) { 9240f66f451Sopenharmony_ci if (!temp) temp = i; 9250f66f451Sopenharmony_ci c = ' '; 9260f66f451Sopenharmony_ci } else if (!TT.tty && c<' ') c = '?'; 9270f66f451Sopenharmony_ci buf[i] = c; 9280f66f451Sopenharmony_ci } 9290f66f451Sopenharmony_ci } else *buf = len = 0; 9300f66f451Sopenharmony_ci 9310f66f451Sopenharmony_ci // Store end of argv[0] so ARGS and CMDLINE can differ. 9320f66f451Sopenharmony_ci // We do it for each file string slot but last is cmdline, which sticks. 9330f66f451Sopenharmony_ci slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL 9340f66f451Sopenharmony_ci } 9350f66f451Sopenharmony_ci 9360f66f451Sopenharmony_ci // Each case above calculated/retained len, so we don't need to re-strlen. 9370f66f451Sopenharmony_ci buf += len+1; 9380f66f451Sopenharmony_ci } 9390f66f451Sopenharmony_ci 9400f66f451Sopenharmony_ci // Record that we saw another process, and display/return now if appropriate 9410f66f451Sopenharmony_ci TT.kcount++; 9420f66f451Sopenharmony_ci if (TT.show_process && !TT.threadparent) { 9430f66f451Sopenharmony_ci TT.show_process(tb); 9440f66f451Sopenharmony_ci 9450f66f451Sopenharmony_ci return 0; 9460f66f451Sopenharmony_ci } 9470f66f451Sopenharmony_ci 9480f66f451Sopenharmony_ci // We're retaining data (probably to sort it), save copy in list. 9490f66f451Sopenharmony_ci s = xmalloc(buf-toybuf); 9500f66f451Sopenharmony_ci new->extra = (long)s; 9510f66f451Sopenharmony_ci memcpy(s, toybuf, buf-toybuf); 9520f66f451Sopenharmony_ci 9530f66f451Sopenharmony_ci return DIRTREE_SAVE; 9540f66f451Sopenharmony_ci} 9550f66f451Sopenharmony_ci 9560f66f451Sopenharmony_ci// wrapper for get_ps() that also collects threads under each processes 9570f66f451Sopenharmony_cistatic int get_threads(struct dirtree *new) 9580f66f451Sopenharmony_ci{ 9590f66f451Sopenharmony_ci struct dirtree *dt; 9600f66f451Sopenharmony_ci struct procpid *tb; 9610f66f451Sopenharmony_ci unsigned pid, kcount; 9620f66f451Sopenharmony_ci 9630f66f451Sopenharmony_ci if (!new->parent) return get_ps(new); 9640f66f451Sopenharmony_ci pid = atol(new->name); 9650f66f451Sopenharmony_ci 9660f66f451Sopenharmony_ci TT.threadparent = new; 9670f66f451Sopenharmony_ci if (!get_ps(new)) { 9680f66f451Sopenharmony_ci // it exited out from under us 9690f66f451Sopenharmony_ci TT.threadparent = 0; 9700f66f451Sopenharmony_ci 9710f66f451Sopenharmony_ci return 0; 9720f66f451Sopenharmony_ci } 9730f66f451Sopenharmony_ci 9740f66f451Sopenharmony_ci // Recurse down into tasks, retaining thread groups. 9750f66f451Sopenharmony_ci // Disable show_process at least until we can calculate tcount 9760f66f451Sopenharmony_ci kcount = TT.kcount; 9770f66f451Sopenharmony_ci sprintf(toybuf, "/proc/%u/task", pid); 9780f66f451Sopenharmony_ci new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); 9790f66f451Sopenharmony_ci if (new->child == DIRTREE_ABORTVAL) new->child = 0; 9800f66f451Sopenharmony_ci TT.threadparent = 0; 9810f66f451Sopenharmony_ci kcount = TT.kcount-kcount+1; 9820f66f451Sopenharmony_ci tb = (void *)new->extra; 9830f66f451Sopenharmony_ci tb->slot[SLOT_tcount] = kcount; 9840f66f451Sopenharmony_ci 9850f66f451Sopenharmony_ci // Fill out tid and thread count for each entry in group (if it didn't exit 9860f66f451Sopenharmony_ci // out from under us again; asynchronous reads of unlocked data are fun!) 9870f66f451Sopenharmony_ci if (new->child) for (dt = new->child->child; dt; dt = dt->next) { 9880f66f451Sopenharmony_ci tb = (void *)dt->extra; 9890f66f451Sopenharmony_ci tb->slot[SLOT_pid] = pid; 9900f66f451Sopenharmony_ci tb->slot[SLOT_tcount] = kcount; 9910f66f451Sopenharmony_ci } 9920f66f451Sopenharmony_ci 9930f66f451Sopenharmony_ci // Save or display 9940f66f451Sopenharmony_ci if (!TT.show_process) return DIRTREE_SAVE; 9950f66f451Sopenharmony_ci TT.show_process((void *)new->extra); 9960f66f451Sopenharmony_ci if ((dt = new->child)) { 9970f66f451Sopenharmony_ci new->child = 0; 9980f66f451Sopenharmony_ci while (dt->child) { 9990f66f451Sopenharmony_ci new = dt->child->next; 10000f66f451Sopenharmony_ci TT.show_process((void *)dt->child->extra); 10010f66f451Sopenharmony_ci free(dt->child); 10020f66f451Sopenharmony_ci dt->child = new; 10030f66f451Sopenharmony_ci } 10040f66f451Sopenharmony_ci free(dt); 10050f66f451Sopenharmony_ci } 10060f66f451Sopenharmony_ci 10070f66f451Sopenharmony_ci return 0; 10080f66f451Sopenharmony_ci} 10090f66f451Sopenharmony_ci 10100f66f451Sopenharmony_ci// Parse one FIELD argument (with optional =name :width) into struct ofields 10110f66f451Sopenharmony_cistatic char *parse_ko(void *data, char *type, int length) 10120f66f451Sopenharmony_ci{ 10130f66f451Sopenharmony_ci struct ofields *field; 10140f66f451Sopenharmony_ci char *width, *title, *end, *s; 10150f66f451Sopenharmony_ci int i, j, k; 10160f66f451Sopenharmony_ci 10170f66f451Sopenharmony_ci // Caller's WOULD_EXIT catches -o help and prints help 10180f66f451Sopenharmony_ci if (length==4 && !strncasecmp(type, "HELP", length)) xexit(); 10190f66f451Sopenharmony_ci 10200f66f451Sopenharmony_ci // Get title, length of title, type, end of type, and display width 10210f66f451Sopenharmony_ci 10220f66f451Sopenharmony_ci // Chip off =name to display 10230f66f451Sopenharmony_ci if ((end = strchr(type, '=')) && length>(end-type)) { 10240f66f451Sopenharmony_ci title = end+1; 10250f66f451Sopenharmony_ci length -= (end-type)+1; 10260f66f451Sopenharmony_ci } else { 10270f66f451Sopenharmony_ci end = type+length; 10280f66f451Sopenharmony_ci title = 0; 10290f66f451Sopenharmony_ci } 10300f66f451Sopenharmony_ci 10310f66f451Sopenharmony_ci // Chip off :width to display 10320f66f451Sopenharmony_ci if ((width = strchr(type, ':')) && width<end) { 10330f66f451Sopenharmony_ci if (!title) length = width-type; 10340f66f451Sopenharmony_ci } else width = 0; 10350f66f451Sopenharmony_ci 10360f66f451Sopenharmony_ci // Allocate structure plus extra space to append a copy of title data 10370f66f451Sopenharmony_ci // (this way it's same lifetime, freeing struct automatically frees title) 10380f66f451Sopenharmony_ci field = xzalloc(sizeof(struct ofields)+(length+1)*!!title); 10390f66f451Sopenharmony_ci if (title) { 10400f66f451Sopenharmony_ci memcpy(field->title = (char *)(field+1), title, length); 10410f66f451Sopenharmony_ci field->title[field->len = length] = 0; 10420f66f451Sopenharmony_ci } 10430f66f451Sopenharmony_ci 10440f66f451Sopenharmony_ci if (width) { 10450f66f451Sopenharmony_ci field->len = strtol(++width, &title, 10); 10460f66f451Sopenharmony_ci if (!isdigit(*width) || title != end) return title; 10470f66f451Sopenharmony_ci end = --width; 10480f66f451Sopenharmony_ci } 10490f66f451Sopenharmony_ci 10500f66f451Sopenharmony_ci // Find type 10510f66f451Sopenharmony_ci field->reverse = 1; 10520f66f451Sopenharmony_ci if (*type == '-') field->reverse = -1; 10530f66f451Sopenharmony_ci else if (*type != '+') type--; 10540f66f451Sopenharmony_ci type++; 10550f66f451Sopenharmony_ci for (i = 0; i<ARRAY_LEN(typos); i++) { 10560f66f451Sopenharmony_ci field->which = i; 10570f66f451Sopenharmony_ci for (j = 0; j<2; j++) { 10580f66f451Sopenharmony_ci if (!j) s = typos[i].name; 10590f66f451Sopenharmony_ci // posix requires alternate names for some fields 10600f66f451Sopenharmony_ci else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU, 10610f66f451Sopenharmony_ci PS_VSZ, PS_USER, 0}, i))) continue; 10620f66f451Sopenharmony_ci else 10630f66f451Sopenharmony_ci s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k]; 10640f66f451Sopenharmony_ci 10650f66f451Sopenharmony_ci if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break; 10660f66f451Sopenharmony_ci } 10670f66f451Sopenharmony_ci if (j!=2) break; 10680f66f451Sopenharmony_ci } 10690f66f451Sopenharmony_ci if (i==ARRAY_LEN(typos)) return type; 10700f66f451Sopenharmony_ci if (!field->title) field->title = typos[field->which].name; 10710f66f451Sopenharmony_ci if (!field->len) field->len = typos[field->which].width; 10720f66f451Sopenharmony_ci else if (typos[field->which].width<0) field->len *= -1; 10730f66f451Sopenharmony_ci dlist_add_nomalloc(data, (void *)field); 10740f66f451Sopenharmony_ci 10750f66f451Sopenharmony_ci return 0; 10760f66f451Sopenharmony_ci} 10770f66f451Sopenharmony_ci 10780f66f451Sopenharmony_ci// Write FIELD list into display header string (truncating at blen), 10790f66f451Sopenharmony_ci// and return bitfield of which FIELDs are used. 10800f66f451Sopenharmony_cistatic long long get_headers(struct ofields *field, char *buf, int blen) 10810f66f451Sopenharmony_ci{ 10820f66f451Sopenharmony_ci long long bits = 0; 10830f66f451Sopenharmony_ci int len = 0; 10840f66f451Sopenharmony_ci 10850f66f451Sopenharmony_ci for (; field; field = field->next) { 10860f66f451Sopenharmony_ci len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len, 10870f66f451Sopenharmony_ci field->title); 10880f66f451Sopenharmony_ci bits |= 1LL<<field->which; 10890f66f451Sopenharmony_ci } 10900f66f451Sopenharmony_ci 10910f66f451Sopenharmony_ci return bits; 10920f66f451Sopenharmony_ci} 10930f66f451Sopenharmony_ci 10940f66f451Sopenharmony_ci// Parse command line options -p -s -t -u -U -g -G 10950f66f451Sopenharmony_cistatic char *parse_rest(void *data, char *str, int len) 10960f66f451Sopenharmony_ci{ 10970f66f451Sopenharmony_ci struct ptr_len *pl = (struct ptr_len *)data; 10980f66f451Sopenharmony_ci long *ll = pl->ptr; 10990f66f451Sopenharmony_ci char *end; 11000f66f451Sopenharmony_ci int num = 0; 11010f66f451Sopenharmony_ci 11020f66f451Sopenharmony_ci // Allocate next chunk of data 11030f66f451Sopenharmony_ci if (!(15&pl->len)) 11040f66f451Sopenharmony_ci ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16)); 11050f66f451Sopenharmony_ci 11060f66f451Sopenharmony_ci // Parse numerical input 11070f66f451Sopenharmony_ci if (isdigit(*str)) { 11080f66f451Sopenharmony_ci ll[pl->len] = xstrtol(str, &end, 10); 11090f66f451Sopenharmony_ci if (end==(len+str)) num++; 11100f66f451Sopenharmony_ci // For pkill, -s 0 represents pkill's session id. 11110f66f451Sopenharmony_ci if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0); 11120f66f451Sopenharmony_ci } 11130f66f451Sopenharmony_ci 11140f66f451Sopenharmony_ci if (pl==&TT.pp || pl==&TT.ss) { 11150f66f451Sopenharmony_ci if (num && ll[pl->len]>0) { 11160f66f451Sopenharmony_ci pl->len++; 11170f66f451Sopenharmony_ci 11180f66f451Sopenharmony_ci return 0; 11190f66f451Sopenharmony_ci } 11200f66f451Sopenharmony_ci } else if (pl==&TT.tt) { 11210f66f451Sopenharmony_ci // -t pts = 12,pts/12 tty = /dev/tty2,tty2,S0 11220f66f451Sopenharmony_ci if (!num) { 11230f66f451Sopenharmony_ci if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5; 11240f66f451Sopenharmony_ci if (strstart(&str, "pts/")) { 11250f66f451Sopenharmony_ci len -= 4; 11260f66f451Sopenharmony_ci num++; 11270f66f451Sopenharmony_ci } else if (strstart(&str, "tty")) len -= 3; 11280f66f451Sopenharmony_ci } 11290f66f451Sopenharmony_ci if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) { 11300f66f451Sopenharmony_ci struct stat st; 11310f66f451Sopenharmony_ci 11320f66f451Sopenharmony_ci end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty"); 11330f66f451Sopenharmony_ci memcpy(end, str, len); 11340f66f451Sopenharmony_ci end[len] = 0; 11350f66f451Sopenharmony_ci xstat(toybuf, &st); 11360f66f451Sopenharmony_ci ll[pl->len++] = st.st_rdev; 11370f66f451Sopenharmony_ci 11380f66f451Sopenharmony_ci return 0; 11390f66f451Sopenharmony_ci } 11400f66f451Sopenharmony_ci } else if (len<255) { 11410f66f451Sopenharmony_ci char name[256]; 11420f66f451Sopenharmony_ci 11430f66f451Sopenharmony_ci if (num) { 11440f66f451Sopenharmony_ci pl->len++; 11450f66f451Sopenharmony_ci 11460f66f451Sopenharmony_ci return 0; 11470f66f451Sopenharmony_ci } 11480f66f451Sopenharmony_ci 11490f66f451Sopenharmony_ci memcpy(name, str, len); 11500f66f451Sopenharmony_ci name[len] = 0; 11510f66f451Sopenharmony_ci if (pl==&TT.gg || pl==&TT.GG) { 11520f66f451Sopenharmony_ci struct group *gr = getgrnam(name); 11530f66f451Sopenharmony_ci if (gr) { 11540f66f451Sopenharmony_ci ll[pl->len++] = gr->gr_gid; 11550f66f451Sopenharmony_ci 11560f66f451Sopenharmony_ci return 0; 11570f66f451Sopenharmony_ci } 11580f66f451Sopenharmony_ci } else if (pl==&TT.uu || pl==&TT.UU) { 11590f66f451Sopenharmony_ci struct passwd *pw = getpwnam(name); 11600f66f451Sopenharmony_ci if (pw) { 11610f66f451Sopenharmony_ci ll[pl->len++] = pw->pw_uid; 11620f66f451Sopenharmony_ci 11630f66f451Sopenharmony_ci return 0; 11640f66f451Sopenharmony_ci } 11650f66f451Sopenharmony_ci } 11660f66f451Sopenharmony_ci } 11670f66f451Sopenharmony_ci 11680f66f451Sopenharmony_ci // Return error 11690f66f451Sopenharmony_ci return str; 11700f66f451Sopenharmony_ci} 11710f66f451Sopenharmony_ci 11720f66f451Sopenharmony_ci// sort processes by FIELD(s) listed in option -k 11730f66f451Sopenharmony_cistatic int ksort(void *aa, void *bb) 11740f66f451Sopenharmony_ci{ 11750f66f451Sopenharmony_ci struct ofields *field; 11760f66f451Sopenharmony_ci struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb; 11770f66f451Sopenharmony_ci int ret = 0, slot; 11780f66f451Sopenharmony_ci 11790f66f451Sopenharmony_ci for (field = TT.kfields; field && !ret; field = field->next) { 11800f66f451Sopenharmony_ci slot = typos[field->which].slot; 11810f66f451Sopenharmony_ci 11820f66f451Sopenharmony_ci // Can we do numeric sort? 11830f66f451Sopenharmony_ci if (!(slot&XX)) { 11840f66f451Sopenharmony_ci if (ta->slot[slot]<tb->slot[slot]) ret = -1; 11850f66f451Sopenharmony_ci if (ta->slot[slot]>tb->slot[slot]) ret = 1; 11860f66f451Sopenharmony_ci } 11870f66f451Sopenharmony_ci 11880f66f451Sopenharmony_ci // fallback to string sort 11890f66f451Sopenharmony_ci if (!ret) { 11900f66f451Sopenharmony_ci memccpy(toybuf, string_field(ta, field), 0, 2048); 11910f66f451Sopenharmony_ci toybuf[2048] = 0; 11920f66f451Sopenharmony_ci ret = strcmp(toybuf, string_field(tb, field)); 11930f66f451Sopenharmony_ci } 11940f66f451Sopenharmony_ci ret *= field->reverse; 11950f66f451Sopenharmony_ci } 11960f66f451Sopenharmony_ci 11970f66f451Sopenharmony_ci return ret; 11980f66f451Sopenharmony_ci} 11990f66f451Sopenharmony_ci 12000f66f451Sopenharmony_ci// Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array 12010f66f451Sopenharmony_ci// (recursion because tree from get_thread() isn't flat list of siblings) 12020f66f451Sopenharmony_cistatic struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) 12030f66f451Sopenharmony_ci{ 12040f66f451Sopenharmony_ci while (dt) { 12050f66f451Sopenharmony_ci struct dirtree *next = dt->next; 12060f66f451Sopenharmony_ci 12070f66f451Sopenharmony_ci if (dt->extra) *(tb++) = (void *)dt->extra; 12080f66f451Sopenharmony_ci if (dt->child) tb = collate_leaves(tb, dt->child); 12090f66f451Sopenharmony_ci free(dt); 12100f66f451Sopenharmony_ci dt = next; 12110f66f451Sopenharmony_ci } 12120f66f451Sopenharmony_ci 12130f66f451Sopenharmony_ci return tb; 12140f66f451Sopenharmony_ci} 12150f66f451Sopenharmony_ci 12160f66f451Sopenharmony_ci// Allocate struct procpid array of length count and populate it with ->extra 12170f66f451Sopenharmony_ci// fields from dirtree leaf nodes. (top diffs old & new array to show changes) 12180f66f451Sopenharmony_cistatic struct procpid **collate(int count, struct dirtree *dt) 12190f66f451Sopenharmony_ci{ 12200f66f451Sopenharmony_ci struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *)); 12210f66f451Sopenharmony_ci 12220f66f451Sopenharmony_ci collate_leaves(tbsort, dt); 12230f66f451Sopenharmony_ci 12240f66f451Sopenharmony_ci return tbsort; 12250f66f451Sopenharmony_ci} 12260f66f451Sopenharmony_ci 12270f66f451Sopenharmony_ci// parse command line arguments (ala -k -o) with a comma separated FIELD list 12280f66f451Sopenharmony_cistatic void default_ko(char *s, void *fields, char *err, struct arg_list *arg) 12290f66f451Sopenharmony_ci{ 12300f66f451Sopenharmony_ci struct arg_list def; 12310f66f451Sopenharmony_ci int x; 12320f66f451Sopenharmony_ci 12330f66f451Sopenharmony_ci memset(&def, 0, sizeof(struct arg_list)); 12340f66f451Sopenharmony_ci def.arg = s; 12350f66f451Sopenharmony_ci WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko)); 12360f66f451Sopenharmony_ci if (x) help_help(); 12370f66f451Sopenharmony_ci} 12380f66f451Sopenharmony_ci 12390f66f451Sopenharmony_ci// There link ps & top to shell cmd "task". 12400f66f451Sopenharmony_cistatic void link_shell_cmd_task() 12410f66f451Sopenharmony_ci{ 12420f66f451Sopenharmony_ci const char *cmdName = "task"; 12430f66f451Sopenharmony_ci char *cmdLine = NULL; 12440f66f451Sopenharmony_ci 12450f66f451Sopenharmony_ci if ( toys.argv[1] && !strcmp(toys.argv[1], "-a")) { 12460f66f451Sopenharmony_ci cmdLine = "task -a"; 12470f66f451Sopenharmony_ci (void)syscall(__NR_shellexec, cmdName, cmdLine); 12480f66f451Sopenharmony_ci } 12490f66f451Sopenharmony_ci else 12500f66f451Sopenharmony_ci { 12510f66f451Sopenharmony_ci cmdLine = "task"; 12520f66f451Sopenharmony_ci (void)syscall(__NR_shellexec, cmdName, cmdLine); 12530f66f451Sopenharmony_ci } 12540f66f451Sopenharmony_ci} 12550f66f451Sopenharmony_civoid ps_main(void) 12560f66f451Sopenharmony_ci{ 12570f66f451Sopenharmony_ci link_shell_cmd_task(); 12580f66f451Sopenharmony_ci} 12590f66f451Sopenharmony_ci 12600f66f451Sopenharmony_ci#define CLEANUP_ps 12610f66f451Sopenharmony_ci#define FOR_top 12620f66f451Sopenharmony_ci#include "generated/flags.h" 12630f66f451Sopenharmony_ci 12640f66f451Sopenharmony_ci// select which of the -o fields to sort by 12650f66f451Sopenharmony_cistatic void setsort(int pos) 12660f66f451Sopenharmony_ci{ 12670f66f451Sopenharmony_ci struct ofields *field, *field2; 12680f66f451Sopenharmony_ci int i = 0; 12690f66f451Sopenharmony_ci 12700f66f451Sopenharmony_ci if (pos<0) pos = 0; 12710f66f451Sopenharmony_ci 12720f66f451Sopenharmony_ci for (field = TT.fields; field; field = field->next) { 12730f66f451Sopenharmony_ci if ((TT.sortpos = i++)<pos && field->next) continue; 12740f66f451Sopenharmony_ci field2 = TT.kfields; 12750f66f451Sopenharmony_ci field2->which = field->which; 12760f66f451Sopenharmony_ci field2->len = field->len; 12770f66f451Sopenharmony_ci break; 12780f66f451Sopenharmony_ci } 12790f66f451Sopenharmony_ci} 12800f66f451Sopenharmony_ci 12810f66f451Sopenharmony_ci// If we have both, adjust slot[deltas[]] to be relative to previous 12820f66f451Sopenharmony_ci// measurement rather than process start. Stomping old.data is fine 12830f66f451Sopenharmony_ci// because we free it after displaying. 12840f66f451Sopenharmony_cistatic int merge_deltas(long long *oslot, long long *nslot, int milis) 12850f66f451Sopenharmony_ci{ 12860f66f451Sopenharmony_ci char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar, 12870f66f451Sopenharmony_ci SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap}; 12880f66f451Sopenharmony_ci int i; 12890f66f451Sopenharmony_ci 12900f66f451Sopenharmony_ci for (i = 0; i<ARRAY_LEN(deltas); i++) 12910f66f451Sopenharmony_ci oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]]; 12920f66f451Sopenharmony_ci oslot[SLOT_upticks] = (milis*TT.ticks)/1000; 12930f66f451Sopenharmony_ci 12940f66f451Sopenharmony_ci return 1; 12950f66f451Sopenharmony_ci} 12960f66f451Sopenharmony_ci 12970f66f451Sopenharmony_cistatic int header_line(int line, int rev) 12980f66f451Sopenharmony_ci{ 12990f66f451Sopenharmony_ci if (!line) return 0; 13000f66f451Sopenharmony_ci 13010f66f451Sopenharmony_ci if (FLAG(b)) puts(toybuf); 13020f66f451Sopenharmony_ci else { 13030f66f451Sopenharmony_ci printf("%s%-*.*s%s\r\n", rev?"\033[7m":"", rev?TT.width:0, TT.width, toybuf, 13040f66f451Sopenharmony_ci rev?"\033[0m":""); 13050f66f451Sopenharmony_ci } 13060f66f451Sopenharmony_ci 13070f66f451Sopenharmony_ci return line-1; 13080f66f451Sopenharmony_ci} 13090f66f451Sopenharmony_ci 13100f66f451Sopenharmony_cistatic void top_cursor_cleanup(void) 13110f66f451Sopenharmony_ci{ 13120f66f451Sopenharmony_ci tty_esc("?25h"); 13130f66f451Sopenharmony_ci} 13140f66f451Sopenharmony_ci 13150f66f451Sopenharmony_cistatic void top_common( 13160f66f451Sopenharmony_ci int (*filter)(long long *oslot, long long *nslot, int milis)) 13170f66f451Sopenharmony_ci{ 13180f66f451Sopenharmony_ci long long timeout = 0, now, stats[16]; 13190f66f451Sopenharmony_ci struct proclist { 13200f66f451Sopenharmony_ci struct procpid **tb; 13210f66f451Sopenharmony_ci int count; 13220f66f451Sopenharmony_ci long long whence; 13230f66f451Sopenharmony_ci } plist[2], *plold, *plnew, old, new, mix; 13240f66f451Sopenharmony_ci char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle", 13250f66f451Sopenharmony_ci "iow", "irq", "sirq", "host"}; 13260f66f451Sopenharmony_ci unsigned tock = 0; 13270f66f451Sopenharmony_ci int i, lines, topoff = 0, done = 0; 13280f66f451Sopenharmony_ci char stdout_buf[BUFSIZ]; 13290f66f451Sopenharmony_ci 13300f66f451Sopenharmony_ci if (!TT.fields) perror_exit("no -o"); 13310f66f451Sopenharmony_ci 13320f66f451Sopenharmony_ci // Avoid flicker and hide the cursor in interactive mode. 13330f66f451Sopenharmony_ci if (!FLAG(b)) { 13340f66f451Sopenharmony_ci setbuf(stdout, stdout_buf); 13350f66f451Sopenharmony_ci sigatexit(top_cursor_cleanup); 13360f66f451Sopenharmony_ci tty_esc("?25l"); 13370f66f451Sopenharmony_ci } 13380f66f451Sopenharmony_ci 13390f66f451Sopenharmony_ci toys.signal = SIGWINCH; 13400f66f451Sopenharmony_ci TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf)); 13410f66f451Sopenharmony_ci *scratch = 0; 13420f66f451Sopenharmony_ci memset(plist, 0, sizeof(plist)); 13430f66f451Sopenharmony_ci memset(stats, 0, sizeof(stats)); 13440f66f451Sopenharmony_ci do { 13450f66f451Sopenharmony_ci struct dirtree *dt; 13460f66f451Sopenharmony_ci int recalc = 1; 13470f66f451Sopenharmony_ci 13480f66f451Sopenharmony_ci plold = plist+(tock++&1); 13490f66f451Sopenharmony_ci plnew = plist+(tock&1); 13500f66f451Sopenharmony_ci plnew->whence = millitime(); 13510f66f451Sopenharmony_ci dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, 13520f66f451Sopenharmony_ci (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps); 13530f66f451Sopenharmony_ci if (dt == DIRTREE_ABORTVAL) error_exit("no /proc"); 13540f66f451Sopenharmony_ci plnew->tb = collate(plnew->count = TT.kcount, dt); 13550f66f451Sopenharmony_ci TT.kcount = 0; 13560f66f451Sopenharmony_ci 13570f66f451Sopenharmony_ci if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) { 13580f66f451Sopenharmony_ci long long *st = stats+8*(tock&1); 13590f66f451Sopenharmony_ci 13600f66f451Sopenharmony_ci // user nice system idle iowait irq softirq host 13610f66f451Sopenharmony_ci sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", 13620f66f451Sopenharmony_ci st, st+1, st+2, st+3, st+4, st+5, st+6, st+7); 13630f66f451Sopenharmony_ci } 13640f66f451Sopenharmony_ci 13650f66f451Sopenharmony_ci // First time, wait a quarter of a second to collect a little delta data. 13660f66f451Sopenharmony_ci if (!plold->tb) { 13670f66f451Sopenharmony_ci msleep(250); 13680f66f451Sopenharmony_ci continue; 13690f66f451Sopenharmony_ci } 13700f66f451Sopenharmony_ci 13710f66f451Sopenharmony_ci // Collate old and new into "mix", depends on /proc read in pid sort order 13720f66f451Sopenharmony_ci old = *plold; 13730f66f451Sopenharmony_ci new = *plnew; 13740f66f451Sopenharmony_ci mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid)); 13750f66f451Sopenharmony_ci mix.count = 0; 13760f66f451Sopenharmony_ci 13770f66f451Sopenharmony_ci while (old.count || new.count) { 13780f66f451Sopenharmony_ci struct procpid *otb = old.count ? *old.tb : 0, 13790f66f451Sopenharmony_ci *ntb = new.count ? *new.tb : 0; 13800f66f451Sopenharmony_ci 13810f66f451Sopenharmony_ci // If we just have old for this process, it exited. Discard it. 13820f66f451Sopenharmony_ci if (old.count && (!new.count || *otb->slot < *ntb->slot)) { 13830f66f451Sopenharmony_ci old.tb++; 13840f66f451Sopenharmony_ci old.count--; 13850f66f451Sopenharmony_ci 13860f66f451Sopenharmony_ci continue; 13870f66f451Sopenharmony_ci } 13880f66f451Sopenharmony_ci 13890f66f451Sopenharmony_ci // If we just have new, use it verbatim 13900f66f451Sopenharmony_ci if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb; 13910f66f451Sopenharmony_ci else { 13920f66f451Sopenharmony_ci // Keep or discard 13930f66f451Sopenharmony_ci if (filter(otb->slot, ntb->slot, new.whence-old.whence)) { 13940f66f451Sopenharmony_ci mix.tb[mix.count] = otb; 13950f66f451Sopenharmony_ci mix.count++; 13960f66f451Sopenharmony_ci } 13970f66f451Sopenharmony_ci old.tb++; 13980f66f451Sopenharmony_ci old.count--; 13990f66f451Sopenharmony_ci } 14000f66f451Sopenharmony_ci new.tb++; 14010f66f451Sopenharmony_ci new.count--; 14020f66f451Sopenharmony_ci } 14030f66f451Sopenharmony_ci 14040f66f451Sopenharmony_ci // Don't re-fetch data if it's not time yet, just re-display existing data. 14050f66f451Sopenharmony_ci for (;;) { 14060f66f451Sopenharmony_ci char was, is; 14070f66f451Sopenharmony_ci 14080f66f451Sopenharmony_ci if (recalc) { 14090f66f451Sopenharmony_ci qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort); 14100f66f451Sopenharmony_ci if (!FLAG(b)) { 14110f66f451Sopenharmony_ci printf("\033[H\033[J"); 14120f66f451Sopenharmony_ci if (toys.signal) { 14130f66f451Sopenharmony_ci toys.signal = 0; 14140f66f451Sopenharmony_ci terminal_probesize(&TT.width, &TT.height); 14150f66f451Sopenharmony_ci } 14160f66f451Sopenharmony_ci } 14170f66f451Sopenharmony_ci if (TT.top.m) TT.height = TT.top.m+5; 14180f66f451Sopenharmony_ci lines = TT.height; 14190f66f451Sopenharmony_ci } 14200f66f451Sopenharmony_ci if (recalc && !FLAG(q)) { 14210f66f451Sopenharmony_ci // Display "top" header. 14220f66f451Sopenharmony_ci if (*toys.which->name == 't') { 14230f66f451Sopenharmony_ci struct ofields field; 14240f66f451Sopenharmony_ci char hr[4][32]; 14250f66f451Sopenharmony_ci long long ll, up = 0; 14260f66f451Sopenharmony_ci long run[6]; 14270f66f451Sopenharmony_ci int j; 14280f66f451Sopenharmony_ci 14290f66f451Sopenharmony_ci // Count running, sleeping, stopped, zombie processes. 14300f66f451Sopenharmony_ci // The kernel has more states (and different sets in different 14310f66f451Sopenharmony_ci // versions), so we need to map them. (R)unning and (Z)ombie are 14320f66f451Sopenharmony_ci // easy enough, and since "stopped" is rare (just T and t as of 14330f66f451Sopenharmony_ci // Linux 4.20), we assume everything else is "sleeping". 14340f66f451Sopenharmony_ci field.which = PS_S; 14350f66f451Sopenharmony_ci memset(run, 0, sizeof(run)); 14360f66f451Sopenharmony_ci for (i = 0; i<mix.count; i++) 14370f66f451Sopenharmony_ci run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++; 14380f66f451Sopenharmony_ci sprintf(toybuf, 14390f66f451Sopenharmony_ci "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, " 14400f66f451Sopenharmony_ci "%3ld zombie", FLAG(H)?"Thread":"Task", mix.count, run[1], run[0], 14410f66f451Sopenharmony_ci run[2]+run[3], run[4]); 14420f66f451Sopenharmony_ci lines = header_line(lines, 0); 14430f66f451Sopenharmony_ci 14440f66f451Sopenharmony_ci if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) { 14450f66f451Sopenharmony_ci for (i = 0; i<6; i++) { 14460f66f451Sopenharmony_ci j = i%3; 14470f66f451Sopenharmony_ci pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:", 14480f66f451Sopenharmony_ci "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]); 14490f66f451Sopenharmony_ci human_readable_long(hr[j+!!j], 1024*(run[i] = pos?atol(pos):0), 14500f66f451Sopenharmony_ci 8, 0); 14510f66f451Sopenharmony_ci if (j==1) human_readable_long(hr[1], 1024*(run[i-1]-run[i]), 8,0); 14520f66f451Sopenharmony_ci else if (j==2) { 14530f66f451Sopenharmony_ci sprintf(toybuf, (i<3) 14540f66f451Sopenharmony_ci ? " Mem: %9s total, %9s used, %9s free, %9s buffers" 14550f66f451Sopenharmony_ci : " Swap: %9s total, %9s used, %9s free, %9s cached", 14560f66f451Sopenharmony_ci hr[0], hr[1], hr[2], hr[3]); 14570f66f451Sopenharmony_ci lines = header_line(lines, 0); 14580f66f451Sopenharmony_ci } 14590f66f451Sopenharmony_ci } 14600f66f451Sopenharmony_ci } 14610f66f451Sopenharmony_ci 14620f66f451Sopenharmony_ci pos = toybuf; 14630f66f451Sopenharmony_ci i = sysconf(_SC_NPROCESSORS_CONF); 14640f66f451Sopenharmony_ci pos += sprintf(pos, "%d%%cpu", i*100); 14650f66f451Sopenharmony_ci j = 4+(i>10); 14660f66f451Sopenharmony_ci 14670f66f451Sopenharmony_ci // If a processor goes idle it's powered down and its idle ticks don't 14680f66f451Sopenharmony_ci // advance, so calculate idle time as potential time - used. 14690f66f451Sopenharmony_ci if (mix.count) up = mix.tb[0]->slot[SLOT_upticks]; 14700f66f451Sopenharmony_ci if (!up) up = 1; 14710f66f451Sopenharmony_ci now = up*i; 14720f66f451Sopenharmony_ci ll = stats[3] = stats[11] = 0; 14730f66f451Sopenharmony_ci for (i = 0; i<8; i++) ll += stats[i]-stats[i+8]; 14740f66f451Sopenharmony_ci stats[3] = now - llabs(ll); 14750f66f451Sopenharmony_ci 14760f66f451Sopenharmony_ci for (i = 0; i<8; i++) { 14770f66f451Sopenharmony_ci ll = (llabs(stats[i]-stats[i+8])*1000)/up; 14780f66f451Sopenharmony_ci pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]); 14790f66f451Sopenharmony_ci } 14800f66f451Sopenharmony_ci lines = header_line(lines, 0); 14810f66f451Sopenharmony_ci } else { 14820f66f451Sopenharmony_ci struct ofields *field; 14830f66f451Sopenharmony_ci struct procpid tb; 14840f66f451Sopenharmony_ci 14850f66f451Sopenharmony_ci memset(&tb, 0, sizeof(struct procpid)); 14860f66f451Sopenharmony_ci pos = stpcpy(toybuf, "Totals:"); 14870f66f451Sopenharmony_ci for (field = TT.fields; field; field = field->next) { 14880f66f451Sopenharmony_ci long long ll, bits = 0; 14890f66f451Sopenharmony_ci int slot = typos[field->which].slot&(XX-1); 14900f66f451Sopenharmony_ci 14910f66f451Sopenharmony_ci if (field->which<PS_C || field->which>PS_DIO) continue; 14920f66f451Sopenharmony_ci ll = 1LL<<field->which; 14930f66f451Sopenharmony_ci if (bits&ll) continue; 14940f66f451Sopenharmony_ci bits |= ll; 14950f66f451Sopenharmony_ci for (i=0; i<mix.count; i++) 14960f66f451Sopenharmony_ci tb.slot[slot] += mix.tb[i]->slot[slot]; 14970f66f451Sopenharmony_ci pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf), 14980f66f451Sopenharmony_ci " %s: %*s,", typos[field->which].name, 14990f66f451Sopenharmony_ci field->len, string_field(&tb, field)); 15000f66f451Sopenharmony_ci } 15010f66f451Sopenharmony_ci *--pos = 0; 15020f66f451Sopenharmony_ci lines = header_line(lines, 0); 15030f66f451Sopenharmony_ci } 15040f66f451Sopenharmony_ci 15050f66f451Sopenharmony_ci get_headers(TT.fields, pos = toybuf, sizeof(toybuf)); 15060f66f451Sopenharmony_ci for (i = 0, is = ' '; *pos; pos++) { 15070f66f451Sopenharmony_ci was = is; 15080f66f451Sopenharmony_ci is = *pos; 15090f66f451Sopenharmony_ci if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf) 15100f66f451Sopenharmony_ci pos[-1] = '['; 15110f66f451Sopenharmony_ci if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']'; 15120f66f451Sopenharmony_ci } 15130f66f451Sopenharmony_ci if (FLAG(b)) while (isspace(*(pos-1))) --pos; 15140f66f451Sopenharmony_ci *pos = 0; 15150f66f451Sopenharmony_ci lines = header_line(lines, 1); 15160f66f451Sopenharmony_ci } 15170f66f451Sopenharmony_ci if (!recalc && !FLAG(b)) 15180f66f451Sopenharmony_ci printf("\033[%dH\033[J", 1+TT.height-lines); 15190f66f451Sopenharmony_ci recalc = 1; 15200f66f451Sopenharmony_ci 15210f66f451Sopenharmony_ci for (i = 0; i<lines && i+topoff<mix.count; i++) { 15220f66f451Sopenharmony_ci // Running processes are shown in bold. 15230f66f451Sopenharmony_ci int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R'; 15240f66f451Sopenharmony_ci 15250f66f451Sopenharmony_ci if (!FLAG(b) && i) putchar('\n'); 15260f66f451Sopenharmony_ci if (bold) printf("\033[1m"); 15270f66f451Sopenharmony_ci show_ps(mix.tb[i+topoff]); 15280f66f451Sopenharmony_ci if (bold) printf("\033[m"); 15290f66f451Sopenharmony_ci } 15300f66f451Sopenharmony_ci 15310f66f451Sopenharmony_ci if (TT.top.n && !--TT.top.n) { 15320f66f451Sopenharmony_ci done++; 15330f66f451Sopenharmony_ci break; 15340f66f451Sopenharmony_ci } 15350f66f451Sopenharmony_ci 15360f66f451Sopenharmony_ci now = millitime(); 15370f66f451Sopenharmony_ci if (timeout<=now) timeout = new.whence+TT.top.d; 15380f66f451Sopenharmony_ci if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d; 15390f66f451Sopenharmony_ci 15400f66f451Sopenharmony_ci // In batch mode, we ignore the keyboard. 15410f66f451Sopenharmony_ci if (FLAG(b)) { 15420f66f451Sopenharmony_ci msleep(timeout-now); 15430f66f451Sopenharmony_ci // Make an obvious gap between datasets. 15440f66f451Sopenharmony_ci xputs("\n\n"); 15450f66f451Sopenharmony_ci break; 15460f66f451Sopenharmony_ci } else fflush(stdout); 15470f66f451Sopenharmony_ci 15480f66f451Sopenharmony_ci i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height); 15490f66f451Sopenharmony_ci if (i==-1 || i==3 || toupper(i)=='Q') { 15500f66f451Sopenharmony_ci done++; 15510f66f451Sopenharmony_ci break; 15520f66f451Sopenharmony_ci } 15530f66f451Sopenharmony_ci if (i==-2) break; 15540f66f451Sopenharmony_ci if (i==-3) continue; 15550f66f451Sopenharmony_ci 15560f66f451Sopenharmony_ci // Flush unknown escape sequences. 15570f66f451Sopenharmony_ci if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height)); 15580f66f451Sopenharmony_ci else if (i=='\r' || i==' ') { 15590f66f451Sopenharmony_ci timeout = 0; 15600f66f451Sopenharmony_ci break; 15610f66f451Sopenharmony_ci } else if (toupper(i)=='R') 15620f66f451Sopenharmony_ci ((struct ofields *)TT.kfields)->reverse *= -1; 15630f66f451Sopenharmony_ci else { 15640f66f451Sopenharmony_ci i -= 256; 15650f66f451Sopenharmony_ci if (i == KEY_LEFT) setsort(TT.sortpos-1); 15660f66f451Sopenharmony_ci else if (i == KEY_RIGHT) setsort(TT.sortpos+1); 15670f66f451Sopenharmony_ci // KEY_UP is 0, so at end of strchr 15680f66f451Sopenharmony_ci else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) { 15690f66f451Sopenharmony_ci recalc = 0; 15700f66f451Sopenharmony_ci 15710f66f451Sopenharmony_ci if (i == KEY_UP) topoff--; 15720f66f451Sopenharmony_ci else if (i == KEY_DOWN) topoff++; 15730f66f451Sopenharmony_ci else if (i == KEY_PGDN) topoff += lines; 15740f66f451Sopenharmony_ci else if (i == KEY_PGUP) topoff -= lines; 15750f66f451Sopenharmony_ci if (topoff<0) topoff = 0; 15760f66f451Sopenharmony_ci if (topoff>mix.count) topoff = mix.count; 15770f66f451Sopenharmony_ci } 15780f66f451Sopenharmony_ci } 15790f66f451Sopenharmony_ci continue; 15800f66f451Sopenharmony_ci } 15810f66f451Sopenharmony_ci 15820f66f451Sopenharmony_ci free(mix.tb); 15830f66f451Sopenharmony_ci for (i=0; i<plold->count; i++) free(plold->tb[i]); 15840f66f451Sopenharmony_ci free(plold->tb); 15850f66f451Sopenharmony_ci } while (!done); 15860f66f451Sopenharmony_ci 15870f66f451Sopenharmony_ci if (!FLAG(b)) tty_reset(); 15880f66f451Sopenharmony_ci} 15890f66f451Sopenharmony_ci 15900f66f451Sopenharmony_cistatic void top_setup(char *defo, char *defk) 15910f66f451Sopenharmony_ci{ 15920f66f451Sopenharmony_ci TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime 15930f66f451Sopenharmony_ci TT.tty = tty_fd() != -1; 15940f66f451Sopenharmony_ci 15950f66f451Sopenharmony_ci // Are we doing "batch" output or interactive? 15960f66f451Sopenharmony_ci if (FLAG(b)) TT.width = TT.height = 99999; 15970f66f451Sopenharmony_ci else { 15980f66f451Sopenharmony_ci // Grab starting time, make terminal raw, switch off cursor, 15990f66f451Sopenharmony_ci // set signal handler to put terminal/cursor back to normal at exit. 16000f66f451Sopenharmony_ci TT.time = millitime(); 16010f66f451Sopenharmony_ci start_redraw(&TT.width, &TT.height); 16020f66f451Sopenharmony_ci } 16030f66f451Sopenharmony_ci 16040f66f451Sopenharmony_ci comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest); 16050f66f451Sopenharmony_ci comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest); 16060f66f451Sopenharmony_ci TT.match_process = shared_match_process; 16070f66f451Sopenharmony_ci 16080f66f451Sopenharmony_ci default_ko(defo, &TT.fields, "bad -o", TT.top.o); 16090f66f451Sopenharmony_ci dlist_terminate(TT.fields); 16100f66f451Sopenharmony_ci 16110f66f451Sopenharmony_ci // First (dummy) sort field is overwritten by setsort() 16120f66f451Sopenharmony_ci default_ko("-S", &TT.kfields, 0, 0); 16130f66f451Sopenharmony_ci default_ko(defk, &TT.kfields, "bad -k", TT.top.k); 16140f66f451Sopenharmony_ci dlist_terminate(TT.kfields); 16150f66f451Sopenharmony_ci setsort(TT.top.s-1); 16160f66f451Sopenharmony_ci} 16170f66f451Sopenharmony_ci 16180f66f451Sopenharmony_ci 16190f66f451Sopenharmony_civoid top_main(void) 16200f66f451Sopenharmony_ci{ 16210f66f451Sopenharmony_ci link_shell_cmd_task(); 16220f66f451Sopenharmony_ci} 16230f66f451Sopenharmony_ci 16240f66f451Sopenharmony_ci#define CLEANUP_top 16250f66f451Sopenharmony_ci#define FOR_iotop 16260f66f451Sopenharmony_ci#include "generated/flags.h" 16270f66f451Sopenharmony_ci 16280f66f451Sopenharmony_ci// Compare old and new proces lists to measure changes 16290f66f451Sopenharmony_cistatic int iotop_filter(long long *oslot, long long *nslot, int milis) 16300f66f451Sopenharmony_ci{ 16310f66f451Sopenharmony_ci // Current I/O, or accumulated since process start? 16320f66f451Sopenharmony_ci if (!FLAG(a)) merge_deltas(oslot, nslot, milis); 16330f66f451Sopenharmony_ci else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000; 16340f66f451Sopenharmony_ci 16350f66f451Sopenharmony_ci return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)]; 16360f66f451Sopenharmony_ci} 16370f66f451Sopenharmony_ci 16380f66f451Sopenharmony_civoid iotop_main(void) 16390f66f451Sopenharmony_ci{ 16400f66f451Sopenharmony_ci char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A); 16410f66f451Sopenharmony_ci 16420f66f451Sopenharmony_ci if (FLAG(K)) TT.forcek++; 16430f66f451Sopenharmony_ci 16440f66f451Sopenharmony_ci top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d), 16450f66f451Sopenharmony_ci s2 = xmprintf("-%sIO,-ETIME,-PID",d)); 16460f66f451Sopenharmony_ci free(s1); 16470f66f451Sopenharmony_ci free(s2); 16480f66f451Sopenharmony_ci top_common(iotop_filter); 16490f66f451Sopenharmony_ci} 16500f66f451Sopenharmony_ci 16510f66f451Sopenharmony_ci// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag 16520f66f451Sopenharmony_ci// context, so force pgrep's flags on even when building pkill standalone. 16530f66f451Sopenharmony_ci// (All the pgrep/pkill functions drop out when building ps standalone.) 16540f66f451Sopenharmony_ci#define FORCE_FLAGS 16550f66f451Sopenharmony_ci#define CLEANUP_iotop 16560f66f451Sopenharmony_ci#define FOR_pgrep 16570f66f451Sopenharmony_ci#include "generated/flags.h" 16580f66f451Sopenharmony_ci 16590f66f451Sopenharmony_cistruct regex_list { 16600f66f451Sopenharmony_ci struct regex_list *next; 16610f66f451Sopenharmony_ci regex_t reg; 16620f66f451Sopenharmony_ci}; 16630f66f451Sopenharmony_ci 16640f66f451Sopenharmony_cistatic void do_pgk(struct procpid *tb) 16650f66f451Sopenharmony_ci{ 16660f66f451Sopenharmony_ci if (TT.pgrep.signal) { 16670f66f451Sopenharmony_ci if (kill(*tb->slot, TT.pgrep.signal)) { 16680f66f451Sopenharmony_ci char *s = num_to_sig(TT.pgrep.signal); 16690f66f451Sopenharmony_ci 16700f66f451Sopenharmony_ci if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal); 16710f66f451Sopenharmony_ci perror_msg("%s->%lld", s, *tb->slot); 16720f66f451Sopenharmony_ci } 16730f66f451Sopenharmony_ci } 16740f66f451Sopenharmony_ci if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) { 16750f66f451Sopenharmony_ci printf("%lld", *tb->slot); 16760f66f451Sopenharmony_ci if (FLAG(l)) 16770f66f451Sopenharmony_ci printf(" %s", tb->str+tb->offset[4]*!!FLAG(f)); 16780f66f451Sopenharmony_ci 16790f66f451Sopenharmony_ci printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n"); 16800f66f451Sopenharmony_ci } 16810f66f451Sopenharmony_ci} 16820f66f451Sopenharmony_ci 16830f66f451Sopenharmony_cistatic void match_pgrep(void *p) 16840f66f451Sopenharmony_ci{ 16850f66f451Sopenharmony_ci struct procpid *tb = p; 16860f66f451Sopenharmony_ci regmatch_t match; 16870f66f451Sopenharmony_ci struct regex_list *reg; 16880f66f451Sopenharmony_ci char *name = tb->str+tb->offset[4]*!!FLAG(f); 16890f66f451Sopenharmony_ci 16900f66f451Sopenharmony_ci // Never match ourselves. 16910f66f451Sopenharmony_ci if (TT.pgrep.self == *tb->slot) return; 16920f66f451Sopenharmony_ci 16930f66f451Sopenharmony_ci if (TT.pgrep.regexes) { 16940f66f451Sopenharmony_ci for (reg = TT.pgrep.regexes; reg; reg = reg->next) { 16950f66f451Sopenharmony_ci if (regexec(®->reg, name, 1, &match, 0)) continue; 16960f66f451Sopenharmony_ci if (FLAG(x)) 16970f66f451Sopenharmony_ci if (match.rm_so || match.rm_eo!=strlen(name)) continue; 16980f66f451Sopenharmony_ci break; 16990f66f451Sopenharmony_ci } 17000f66f451Sopenharmony_ci if (!FLAG(v) == !reg) return; 17010f66f451Sopenharmony_ci } 17020f66f451Sopenharmony_ci 17030f66f451Sopenharmony_ci // pgrep should return success if there's a match. 17040f66f451Sopenharmony_ci toys.exitval = 0; 17050f66f451Sopenharmony_ci 17060f66f451Sopenharmony_ci // Repurpose a field for -c count. 17070f66f451Sopenharmony_ci TT.sortpos++; 17080f66f451Sopenharmony_ci if (FLAG(n)||FLAG(o)) { 17090f66f451Sopenharmony_ci long long ll = tb->slot[SLOT_starttime]; 17100f66f451Sopenharmony_ci 17110f66f451Sopenharmony_ci if (FLAG(o)) ll *= -1; 17120f66f451Sopenharmony_ci if (TT.time && TT.time>ll) return; 17130f66f451Sopenharmony_ci TT.time = ll; 17140f66f451Sopenharmony_ci free(TT.pgrep.snapshot); 17150f66f451Sopenharmony_ci TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf); 17160f66f451Sopenharmony_ci } else do_pgk(tb); 17170f66f451Sopenharmony_ci} 17180f66f451Sopenharmony_ci 17190f66f451Sopenharmony_cistatic int pgrep_match_process(long long *slot) 17200f66f451Sopenharmony_ci{ 17210f66f451Sopenharmony_ci return !FLAG(v) == !!shared_match_process(slot); 17220f66f451Sopenharmony_ci} 17230f66f451Sopenharmony_ci 17240f66f451Sopenharmony_civoid pgrep_main(void) 17250f66f451Sopenharmony_ci{ 17260f66f451Sopenharmony_ci char **arg; 17270f66f451Sopenharmony_ci struct regex_list *reg; 17280f66f451Sopenharmony_ci 17290f66f451Sopenharmony_ci TT.pgrep.self = getpid(); 17300f66f451Sopenharmony_ci 17310f66f451Sopenharmony_ci // No signal names start with "L", so no need for "L: " in optstr. 17320f66f451Sopenharmony_ci if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L))) 17330f66f451Sopenharmony_ci error_exit("bad -L '%s'", TT.pgrep.L); 17340f66f451Sopenharmony_ci 17350f66f451Sopenharmony_ci comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest); 17360f66f451Sopenharmony_ci comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest); 17370f66f451Sopenharmony_ci comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest); 17380f66f451Sopenharmony_ci comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest); 17390f66f451Sopenharmony_ci comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest); 17400f66f451Sopenharmony_ci comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest); 17410f66f451Sopenharmony_ci comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest); 17420f66f451Sopenharmony_ci 17430f66f451Sopenharmony_ci if ((toys.optflags&(FLAG_x|FLAG_f)) || 17440f66f451Sopenharmony_ci !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u))) 17450f66f451Sopenharmony_ci if (!toys.optc) help_exit("No PATTERN"); 17460f66f451Sopenharmony_ci 17470f66f451Sopenharmony_ci if (FLAG(f)) TT.bits |= _PS_CMDLINE; 17480f66f451Sopenharmony_ci for (arg = toys.optargs; *arg; arg++) { 17490f66f451Sopenharmony_ci reg = xmalloc(sizeof(struct regex_list)); 17500f66f451Sopenharmony_ci xregcomp(®->reg, *arg, REG_EXTENDED); 17510f66f451Sopenharmony_ci reg->next = TT.pgrep.regexes; 17520f66f451Sopenharmony_ci TT.pgrep.regexes = reg; 17530f66f451Sopenharmony_ci } 17540f66f451Sopenharmony_ci TT.match_process = pgrep_match_process; 17550f66f451Sopenharmony_ci TT.show_process = match_pgrep; 17560f66f451Sopenharmony_ci 17570f66f451Sopenharmony_ci // pgrep should return failure if there are no matches. 17580f66f451Sopenharmony_ci toys.exitval = 1; 17590f66f451Sopenharmony_ci 17600f66f451Sopenharmony_ci dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps); 17610f66f451Sopenharmony_ci if (FLAG(c)) printf("%d\n", TT.sortpos); 17620f66f451Sopenharmony_ci if (TT.pgrep.snapshot) { 17630f66f451Sopenharmony_ci do_pgk(TT.pgrep.snapshot); 17640f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot); 17650f66f451Sopenharmony_ci } 17660f66f451Sopenharmony_ci if (TT.pgrep.d) xputc('\n'); 17670f66f451Sopenharmony_ci} 17680f66f451Sopenharmony_ci 17690f66f451Sopenharmony_ci#define CLEANUP_pgrep 17700f66f451Sopenharmony_ci#define FOR_pkill 17710f66f451Sopenharmony_ci#include "generated/flags.h" 17720f66f451Sopenharmony_ci 17730f66f451Sopenharmony_civoid pkill_main(void) 17740f66f451Sopenharmony_ci{ 17750f66f451Sopenharmony_ci char **args = toys.optargs; 17760f66f451Sopenharmony_ci 17770f66f451Sopenharmony_ci if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1; 17780f66f451Sopenharmony_ci if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM; 17790f66f451Sopenharmony_ci if (FLAG(V)) TT.tty = 1; 17800f66f451Sopenharmony_ci pgrep_main(); 17810f66f451Sopenharmony_ci} 1782