aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/ps.c211
1 files changed, 139 insertions, 72 deletions
diff --git a/toys/pending/ps.c b/toys/pending/ps.c
index def59b6e..d3548c7e 100644
--- a/toys/pending/ps.c
+++ b/toys/pending/ps.c
@@ -6,59 +6,76 @@
* And http://kernel.org/doc/Documentation/filesystems/proc.txt Table 1-4
* And linux kernel source fs/proc/array.c function do_task_stat()
*
- * Deviations from posix: no -n to specify an alternate /etc/passwd (??!?)
+ * Deviations from posix: no -n because /proc/self/wchan exists.
* Posix says default output should have field named "TTY" but if you "-o tty"
* the same field should be called "TT" which is _INSANE_ and I'm not doing it.
* It also says that -o "args" and "comm" should behave differently but use
* the same title, which is not the same title as the default output. No.
*
* ps aux
- * TODO: -o maj_flt, min_flt
- * TODO: --sort
+ * TODO: -o maj_flt,min_flt,stat(<NLnl+),rss --sort -Z
+ * TODO: att & bsd style "ps -ax" vs "ps ax" behavior difference
+ * TODO: way too many hardwired constants here, how can I generate them?
+ * TODO: ADDR? In 2015? Posix is literally _decades_ behind the times.
+ *
+ * Design issue: the -o fields are an ordered array, and the order is
+ * significant. The array index is used in strawberry->which (consumed
+ * in do_ps()) and in the bitmasks enabling default fields in ps_main().
-USE_PS(NEWTOY(ps, "aAdeflo*[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PS(NEWTOY(ps, "aAdeflo*p*[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN))
config PS
bool "ps"
default n
help
- usage: ps [-Aade] [-fl] [-gG GROUP] [-o FIELD] [-p PID] [-t TTY] [-u USER]
+ usage: ps [-Aade] [-fl] [-gG GROUP] [-o FIELD] [-p PID] [-t TTY] [-uU USER]
List processes.
+ Which processes to show (selections may be comma separated lists):
+
-A All processes
- -a Processes with terminals, except session leaders
- -d Processes that aren't session leaders
+ -a Processes with terminals that aren't session leaders
+ -d All processes that aren't session leaders
-e Same as -A
- -f Full listing
- -l Long listing
-
- -g Processes belonging to these session leaders
- -G Processes with these real group IDs
- -o Show FIELDS for each process
- -p select by PID
- -t select by TTY
- -u select by USER
- -U select by USER
-
- GROUP, FIELD, PID, TTY, and USER are comma separated lists.
-
- OUTPUT (-o) FIELDS:
-
- "UID", "PID", "PPID", "C", "PRI", "NI", "ADDR", "SZ",
- "WCHAN", "STIME", "TTY", "TIME", "CMD", "COMMAND", "ELAPSED", "GROUP",
- "%CPU", "PGID", "RGROUP", "RUSER", "USER", "VSZ"
-
- C Processor utilization for scheduling
- F Process flags (PF_*) from linux source file include/sched.h
- (in octal rather than hex because posix)
- S Process state:
- R (running) S (sleeping) D (disk sleep) T (stopped) t (tracing stop)
- Z (zombie) X (dead) x (dead) K (wakekill) W (waking)
- PID Process id
- PPID Parent process id
- PRI Priority
- UID User id of process owner
+ -g belonging to selected session leaders (not groups: posix says so)
+ -G belonging to selected real GROUP IDs
+ -p selected PIDs
+ -t attached to selected TTYs
+ -u owned by selected USERs
+ -U owned by selected real USERs
+
+ Which FIELDs to show. (Default = -o pid,tty,time,cmd)
+
+ -f Full listing (uid,pid,ppid,c,stime,tty,time,cmd)
+ -l Long listing (f,s,uid,pid,ppid,c,pri,ni,addr,sz,wchan,tty,time,cmd)
+ -o Output the listed FIELDs
+
+ Available -o FIELDs are: F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY
+ TIME CMD COMMAND ELAPSED GROUP %CPU PGID RGROUP RUSER USER VSZ RSS
+
+ STIME
+ TIME CMD COMMAND ELAPSED GROUP %CPU PGID RGROUP RUSER USER VSZ
+
+ ADDR Process instruction pointer
+ C Processor utilization for scheduling
+ F Process flags (PF_*) from linux source file include/sched.h
+ (in octal rather than hex because posix)
+ NI Niceness of process (lower niceness is higher priority)
+ PID Process id
+ PPID Parent process id
+ PRI Priority
+ RSS Resident Set Size (memory used)
+ S Process state:
+ R (running) S (sleeping) D (disk sleep) T (stopped) t (traced)
+ Z (zombie) X (dead) x (dead) K (wakekill) W (waking)
+ SZ Size (4k pages of memory used)
+ TTY Controlling terminal of process
+ UID User id of process owner
+ WCHAN What it's waiting for
+
+ SZ is memory mapped while RSS is pages consumed. ADDR is an address,
+ WCHAN is a name. S shows a single state letter, STAT adds substatus.
Default output is -o PID,TTY,TIME,CMD
With -f USER=UID,PID,PPID,C,STIME,TTY,TIME,CMD
@@ -69,12 +86,13 @@ config PS
#include "toys.h"
GLOBALS(
+ struct arg_list *p;
struct arg_list *o;
unsigned width;
dev_t tty;
void *fields;
- long uptime;
+ long uptime, pidlen, *pids;
)
/*
@@ -89,15 +107,34 @@ GLOBALS(
struct strawberry {
struct strawberry *next, *prev;
short which, len;
- char title[];
+ char *title;
+ char forever[];
};
+static int match_process(long long *slot)
+{
+ long l;
+
+ // skip processes we don't care about.
+ if (TT.pids) {
+ for (l=0; l<TT.pidlen; l++) if (TT.pids[l] == *slot) return 1;
+ return 0;
+ } else {
+ if ((toys.optflags&(FLAG_a|FLAG_d)) && getsid(*slot)==*slot) return 0;
+ if ((toys.optflags&FLAG_a) && !slot[4]) return 0;
+ if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e)) && TT.tty!=slot[4])
+ return 0;
+ }
+
+ return 1;
+}
+
// dirtree callback.
// toybuf used as: 1024 /proc/$PID/stat, 1024 slot[], 2048 /proc/$PID/cmdline
static int do_ps(struct dirtree *new)
{
struct strawberry *field;
- long long *slot = (void *)(toybuf+1024);
+ long long *slot = (void *)(toybuf+1024), ll;
char *name, *s, state;
int nlen, i, fd, len, width = TT.width;
@@ -115,31 +152,32 @@ static int do_ps(struct dirtree *new)
nlen = s++-name;
if (1>sscanf(++s, " %c%n", &state, &i)) return 0;
- // parse numeric fields
+ // parse numeric fields (PID = 0, skip 2, then 4th field goes in slot[1])
for (len = 1; len<100; len++)
if (1>sscanf(s += i, " %lld%n", slot+len, &i)) break;
- // skip entries we don't care about.
- if ((toys.optflags&(FLAG_a|FLAG_d)) && getsid(*slot)==*slot) return 0;
- if ((toys.optflags&FLAG_a) && !slot[4]) return 0;
- if (!(toys.optflags&(FLAG_a|FLAG_d|FLAG_A|FLAG_e)) && TT.tty!=slot[4])
- return 0;
+ // skip processes we don't care about.
+ if (!match_process(slot)) return 0;
+ // Loop through fields
for (field = TT.fields; field; field = field->next) {
char *out = toybuf+2048;
// Default: unsupported (5 "C")
sprintf(out, "-");
- // PID, PPID, PRI, NI, ADDR, SZ
- if (-1 != (i = stridx((char[]){3,4,6,7,8,9,0}, field->which)))
- sprintf(out, ((1<<i)&0x10) ? "%llx" : "%lld",
- slot[((char[]){0,2,16,17,22})[i]]>>(((1<<i)&0x20) ? 12 : 0));
- // F
- else if (!(i = field->which)) sprintf(out, "%llo", slot[7]);
+ // PID, PPID, PRI, NI, ADDR, SZ, RSS
+ if (-1 != (i = stridx((char[]){3,4,6,7,8,9,24,0}, field->which))) {
+ ll = slot[((char[]){0,1,15,16,27,20,21})[i]];
+ if (i == 5) ll >>= 12;
+ else if (i == 6) ll <<= 2;
+ sprintf(out, "%lld", ll);
+ // F (also assignment of i used by later tests)
+ // Posix doesn't specify what flags should say. Man page says
+ // 1 for PF_FORKNOEXEC and 4 for PF_SUPERPRIV from linux/sched.h
+ } else if (!(i = field->which)) sprintf(out, "%llo", (slot[6]>>6)&5);
// S
- else if (i == 1)
- sprintf(out, "%c", state);
+ else if (i == 1) sprintf(out, "%c", state);
// UID and USER
else if (i == 2 || i == 22) {
sprintf(out, "%d", new->st.st_uid);
@@ -153,7 +191,8 @@ static int do_ps(struct dirtree *new)
sprintf(toybuf+512, "%lld/wchan", *slot);
readfileat(dirtree_parentfd(new), toybuf+512, out, 2047);
- // STIME
+ // STIME (11)
+
// TTY
} else if (i==12) {
@@ -182,20 +221,25 @@ static int do_ps(struct dirtree *new)
// TIME ELAPSED
} else if (i==13 || i==16) {
- long seconds = (i==16) ? slot[20] : slot[11]+slot[12], ll = 60*60*24;
-
- seconds /= sysconf(_SC_CLK_TCK);
- if (i==16) seconds = TT.uptime-seconds;
- for (s = out, i = 0; i<4; i++) {
- if (i>1 || seconds > ll)
- s += sprintf(s, (i==3) ? "%02ld" : "%ld%c", seconds/ll, "-::"[i]);
- seconds %= ll;
- ll /= i ? 60 : 24;
+ int unit = 60*60*24, j = sysconf(_SC_CLK_TCK);
+ long long seconds = (i==16) ? (TT.uptime*j)-slot[19] : slot[11]+slot[12];
+
+ seconds /= j;
+ for (s = 0, j = 0; j<4; j++) {
+ // TIME has 3 required fields, ETIME has 2. (Posix!)
+ if (!s && (seconds>unit || j == 1+(i==16))) s = out;
+ if (s) {
+ s += sprintf(s, "%02ld", (long)(seconds/unit));
+ if ((*s = "-::"[j])) s++;
+ }
+ seconds %= unit;
+ unit /= j ? 60 : 24;
}
-//16 "ELAPSED", "GROUP", "%CPU", "PGID", "RGROUP",
+//17 "GROUP", "%CPU", "PGID", "RGROUP",
//21 "RUSER", -, "VSZ"
+ // COMMAND CMD
// Command line limited to 2k displayable. We could dynamically malloc, but
// it'd almost never get used, querying length of a proc file is awkward,
// fixed buffer is nommu friendly... Wait for somebody to complain. :)
@@ -214,11 +258,13 @@ static int do_ps(struct dirtree *new)
}
close(fd);
}
+
if (len<1) sprintf(out, "[%.*s]", nlen, name);
}
+ // Output the field
i = width<field->len ? width : field->len;
- width -= printf(" %*.*s", i, field->next ? i : width, out);
+ width -= printf(" %*.*s" + (field == TT.fields), i, field->next ? i : width, out);
}
xputc('\n');
@@ -233,7 +279,7 @@ void ps_main(void)
*typos[] = {
"F", "S", "UID", "PID", "PPID", "C", "PRI", "NI", "ADDR", "SZ",
"WCHAN", "STIME", "TTY", "TIME", "CMD", "COMMAND", "ELAPSED", "GROUP",
- "%CPU", "PGID", "RGROUP", "RUSER", "USER", "VSZ"
+ "%CPU", "PGID", "RGROUP", "RUSER", "USER", "VSZ", "RSS"
};
int i, fd = -1;
@@ -254,6 +300,7 @@ void ps_main(void)
}
if (fd != -1) close(fd);
+ // Fetch uptime
sysinfo((void *)toybuf);
// Because "TT.uptime = *(long *)toybuf;" triggers a bug in gcc.
{
@@ -261,6 +308,23 @@ void ps_main(void)
TT.uptime = *sigh;
}
+ // pid list via -p
+ if (toys.optflags&FLAG_p) {
+ struct arg_list *pl;
+ char *next, *end, *arg;
+ int len;
+
+ for (pl = TT.p; pl; pl = pl->next) {
+ arg = pl->arg;
+ while ((next = comma_iterate(&arg, &len))) {
+ if (!(15&TT.pidlen))
+ TT.pids = xrealloc(TT.pids, sizeof(long)*(TT.pidlen+16));
+ if ((TT.pids[TT.pidlen++] = xstrtol(next, &end, 10))<1 || end!=next+len)
+ perror_exit("-p '%s'@%ld", pl->arg, 1+end-pl->arg);
+ }
+ }
+ }
+
// Manual field selection via -o
if (toys.optflags&FLAG_o) {
struct arg_list *ol;
@@ -285,9 +349,11 @@ void ps_main(void)
} else width = 0;
// Allocate structure, copy title
- field = xmalloc(sizeof(struct strawberry)+length+1);
- memcpy(field->title, title ? title : type, length);
- field->title[field->len = length] = 0;
+ field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
+ if (title) {
+ memcpy(field->title = field->forever, title, length);
+ field->title[field->len = length] = 0;
+ }
if (width) {
field->len = strtol(++width, &title, 10);
@@ -314,7 +380,8 @@ void ps_main(void)
if (j!=2) break;
}
if (i == ARRAY_LEN(typos)) error_exit("bad -o %.*s", end-type, type);
- if (!*field->title) strcpy(field->title, typos[field->which]);
+ if (!field->title) field->title = typos[field->which];
+ if (!field->len) field->len = widths[field->which];
dlist_add_nomalloc((void *)&TT.fields, (void *)field);
}
}
@@ -335,7 +402,7 @@ void ps_main(void)
field = xmalloc(sizeof(struct strawberry)+strlen(typos[i])+1);
field->which = i;
field->len = widths[i];
- strcpy(field->title, typos[i]);
+ strcpy(field->title = field->forever, typos[i]);
dlist_add_nomalloc((void *)&TT.fields, (void *)field);
}
}
@@ -348,7 +415,7 @@ void ps_main(void)
// right justify F, UID, PID, PPID, PRI, NI, ADDR SZ, TIME, ELAPSED, %CPU
// todo: STIME? C?
if (!((1<<field->which)&0x523dd)) field->len *= -1;
- printf(" %*s", field->len, field->title);
+ printf(" %*s" + (field == TT.fields), field->len, field->title);
// -f prints USER but calls it UID (but "ps -o uid -f" is numeric...?)
if ((toys.optflags&(FLAG_f|FLAG_o))==FLAG_f && field->which==2)