ps.c 65 KB

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