+/*
+ * An implementation of ps. Rewritten somewhat from the UZIX ps tool.
+ *
+ * Deviations from POSIX
+ *
+ * -G: not supported
+ * -u: uses the real uid
+ *
+ * The -o option is not supported
+ *
+ * Meaningless fields should be '-' but few people do this for all cases
+ * and the standard is at odds with reality. We use '-' where others do
+ * but use 0 for things like F.
+ *
+ * We show times as dummies until the kernel gets fixed up to put the
+ * right data in the right places.
+ *
+ * Many XSI extensions are not implemented
+ *
+ * XSI output formats are produced but not all values are available
+ * XSI says command data from the kernel not via grovelling in swap
+ * should be in [] but we don't bother as we don't go digging in swap
+ * anyway.
+ *
+ * BSD ps is a lot nicer in many ways. We should add some BSD functions
+ * if possible.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
#include <proc.h>
#include <pwd.h>
#include <fcntl.h>
-#include <stdio.h>
-#include <strings.h>
-#include <unistd.h>
-#define F_a 0x01 /* all users flag */
-#define F_h 0x02 /* no header flag */
-#define F_r 0x04 /* running only flag */
-#define F_n 0x08 /* numeric output flag */
+#define TTY_MAJOR 2 /* Be nice if we didn't have to
+ just know this */
#define PTABSIZE 128
-int flags;
+static uint16_t optflags;
+#define F_e 1 /* All */
+#define F_N 2 /* Negate */
+#define F_r 4 /* Running */
+#define F_a 8 /* All associated with terminals */
+#define F_p 16 /* pid match */
+#define F_t 32 /* TTY match */
+#define F_u 64 /* UID match */
+#define F_l 128 /* Long */
-char mapstat(char s)
+static uint16_t outflags;
+#define OF_F 1 /* Flags (octal/additive) */
+#define OF_S 2 /* State */
+#define OF_UID 4 /* UID */
+#define OF_PID 8 /* PID */
+#define OF_PPID 16 /* PPID */
+#define OF_C 32 /* CPU utilization */
+#define OF_PRI 64 /* Priority bigger is lower */
+#define OF_NI 128 /* Nice value */
+#define OF_ADDR 256 /* Address */
+#define OF_SZ 512 /* Size in core */
+#define OF_WCHAN 1024 /* Wait channel */
+#define OF_STIME 2048 /* Starting time */
+#define OF_TTY 4096 /* Controlling tty */
+#define OF_TIME 8192 /* Cumulative execution time */
+#define OF_CMD 16384 /* Command line */
+
+static char mapstat(char s)
{
switch (s) {
case P_ZOMBIE:
}
static struct p_tab_buffer ptab[PTABSIZE];
-static int ppid_slot[PTABSIZE];
-static int uid;
+static pid_t ppid_slot[PTABSIZE];
+static uid_t uid;
+static pid_t pid;
+static uint8_t tty;
+
+static pid_t pid_max = 32767; /* FIXME */
+static uid_t uid_max = 32767; /* FIXME */
+
+#define MAXMATCH 16
+struct matchtab {
+ uint8_t num;
+ uint16_t match[MAXMATCH];
+ const char *type;
+};
-void print_header(void)
+static struct matchtab pidtab = {
+ 0,
+ { 0, },
+ "pid"
+};
+
+static struct matchtab uidtab = {
+ 0,
+ { 0, },
+ "uid"
+};
+
+static struct matchtab ttytab = {
+ 0,
+ { 0, },
+ "tty"
+};
+
+static int match(struct matchtab *tab, uint16_t match)
{
- if (!(flags & F_h)) {
- if (flags & F_n)
- printf(" PID\t PPID\t UID\tSTAT\tWCHAN\tALARM\tCOMMAND\n");
- else
- printf("USER\t PID\t PPID\tSTAT\tWCHAN\tALARM\tCOMMAND\n");
+ uint16_t *p = tab->match;
+ uint8_t n = tab->num;
+ while(n--) {
+ if (*p++ == match)
+ return 1;
+ }
+ return 0;
+}
+
+static int addmatch(struct matchtab *tab, uint16_t match)
+{
+ if (tab->num == MAXMATCH) {
+ fprintf(stderr, "ps: too many %s matches.\n", tab->type);
+ exit(1);
}
+ tab->match[tab->num++] = match;
+ return 0;
+}
+
+static uint16_t do_pidmatch(char *arg)
+{
+ unsigned long n;
+ errno = 0;
+ n = strtoul(arg, NULL, 0);
+ if (errno || n == 0 || n > pid_max) {
+ fprintf(stderr, "ps: invalid process id.\n");
+ exit(1);
+ }
+ return n;
+}
+
+static uint16_t do_uidmatch(char *arg)
+{
+ char *p = arg;
+ unsigned long n;
+ struct passwd *pw;
+
+ errno = 0;
+ n = strtoul(arg, NULL, 0);
+ if (errno) {
+ pw = getpwnam(arg);
+ if (pw == NULL) {
+ fprintf(stderr, "ps: unknown user '%s'.\n", arg);
+ exit(1);
+ }
+ n = pw->pw_uid;
+ if (n > uid_max) {
+ fprintf(stderr, "ps: invalid process id.\n");
+ exit(1);
+ }
+ }
+ return n;
+}
+
+static char tbuf[] = "/dev/ttyXXX";
+
+static uint16_t do_ttymatch(char *arg)
+{
+ char *t;
+ struct stat st;
+
+ t = tbuf;
+ if (isdigit(*arg))
+ strlcpy(tbuf + 8, arg, 3);
+ else if (strncmp(arg, "tty", 3) == 0)
+ strlcpy(tbuf + 8, arg + 3, 3);
+ else
+ t = arg;
+ if (stat(t, &st) == -1) {
+ perror(tbuf);
+ exit(1);
+ }
+ if (major(st.st_rdev) != TTY_MAJOR) {
+ fprintf(stderr, "%s: not a tty.\n");
+ exit(1);
+ }
+ return minor(st.st_rdev);
+}
+
+static void scan_match(struct matchtab *m, char *arg, uint16_t (*op)(char *))
+{
+ char *p = arg;
+ char *b;
+ char *work;
+
+ while((b = strtok_r(p, " \t\n,", &work)) != NULL) {
+ p = NULL;
+ addmatch(m, op(b));
+ }
+}
+
+static void add_pidmatch(char *arg)
+{
+ scan_match(&pidtab, arg, do_pidmatch);
+}
+
+
+static void add_uidmatch(char *arg)
+{
+ scan_match(&uidtab, arg, do_uidmatch);
+}
+
+static void add_ttymatch(char *arg)
+{
+ scan_match(&ttytab, arg, do_ttymatch);
+}
+
+static const char *hdrname[] = {
+ "F ",
+ "S ",
+ " UID ",
+ " PID ",
+ " PPID ",
+ " C ",
+ "PRI ",
+ " NI ",
+ " ADDR ",
+ " SZ ",
+ "WCHAN ",
+ "STIME ",
+ " TTY ",
+ " TIME ",
+ "CMD",
+ NULL
+};
+
+static void print_header(void)
+{
+ uint16_t i = 1;
+ uint8_t n = 0;
+ while(hdrname[n]) {
+ if (outflags & i)
+ fputs(hdrname[n], stdout);
+ i <<= 1;
+ n++;
+ }
+ putchar('\n');
+}
+
+static int compute_show_process(struct p_tab *pp)
+{
+ /* -e: select all */
+ if (optflags & F_e)
+ return 1;
+
+ /* -a: all associated with a terminal */
+ if ((optflags & F_a) && pp->p_tty)
+ return 1;
+
+ /* -p: PID match */
+ if ((optflags & F_p) && match(&pidtab, pp->p_pid))
+ return 1;
+
+ /* -u: UID match */
+ if ((optflags & F_u) && match(&uidtab, pp->p_uid))
+ return 1;
+
+ /* -t: TTY match */
+ if ((optflags & F_t) && match(&ttytab, pp->p_tty))
+ return 1;
+
+ if ((optflags & F_r) && (pp->p_status != P_RUNNING && pp->p_status != P_READY))
+ return 0;
+ /* Really we want euid but the euid isn't visible */
+ if (pp->p_uid == uid && pp->p_tty == tty)
+ return 1;
+
+ return 0;
}
int show_process(struct p_tab *pp)
if (pp->p_status == 0)
return 0;
- if ((flags & F_r) && (pp->p_status != P_RUNNING && pp->p_status != P_READY))
- return 0;
+ if (optflags & F_N)
+ return !compute_show_process(pp);
+ else
+ return compute_show_process(pp);
+}
- if (!(flags & F_a) && (pp->p_uid != uid))
- return 0;
+static const char *username(uid_t uid)
+{
+ static char uname[6];
+ struct passwd *pwd = getpwuid(uid);
+ if (pwd)
+ return pwd->pw_name;
+ sprintf(uname, "%d", uid);
+ return uname;
+}
+
+static char tname[7] = "tty";
- return 1;
+static const char *ttyshortname(uint8_t tty)
+{
+ sprintf(tname + 3, "%d", tty);
+ return tname;
}
void display_process(struct p_tab *pp, int i)
{
- struct passwd *pwd;
- static char name[10], uname[20];
int j;
- strncpy(name, pp->p_name, 8);
- name[8] = '\0';
-
- for (j = 0; j < 8; ++j) {
- if (name[j] != 0)
- if (name[j] < ' ')
- name[j] = '?';
+ if (outflags & OF_F)
+ fputs("0 ", stdout);
+ if (outflags & OF_S) {
+ putchar(mapstat(pp->p_status));
+ putchar(' ');
}
-
- if (flags & F_n) {
- printf("%5d\t%5d\t%5d\t%c\t%04x\t%-5d\t%s\n", pp->p_pid, ptab[ppid_slot[i]].p_tab.p_pid, pp->p_uid, mapstat(pp->p_status), pp->p_wait, pp->p_alarm, name);
- } else {
- pwd = getpwuid(pp->p_uid);
- if (pwd)
- strcpy(uname, pwd->pw_name);
+ if (outflags & OF_UID)
+ printf("%5d ", pp->p_uid);
+ if (outflags & OF_PID)
+ printf("%5d ", pp->p_pid);
+ if (outflags & OF_PPID)
+ printf("%5d ", ptab[ppid_slot[i]].p_tab.p_pid);
+ if (outflags & OF_C)
+ fputs(" 0 ", stdout);
+ if (outflags & OF_PRI)
+ printf("%3d ", pp->p_priority);
+ if (outflags & OF_NI)
+ printf("%3d ", pp->p_nice);
+ if (outflags & OF_ADDR)
+ fputs(" - ", stdout);
+ if (outflags & OF_SZ)
+ fputs(" - ", stdout);
+ if (outflags & OF_WCHAN) {
+ if (pp->p_status > 2)
+ printf("%5d ", (unsigned int)pp->p_wait);
else
- sprintf(uname, "%d", pp->p_uid);
- printf("%s\t%5d\t%5d\t%c\t%04x\t%-5d\t%s\n", uname, pp->p_pid, ptab[ppid_slot[i]].p_tab.p_pid, mapstat(pp->p_status), pp->p_wait, pp->p_alarm, name);
+ fputs(" - ", stdout);
+ }
+ /* We need to sort out the whole kernel and user handling of
+ times in ptab verus udata here */
+ if (outflags & OF_STIME)
+ fputs("00:00 ", stdout); /* FIXME */
+ if (outflags & OF_TTY) {
+ if (!pp->p_tty)
+ fputs(" ? ", stdout);
+ else
+ printf("%6s ", ttyshortname(pp->p_tty));
+ }
+ if (outflags & OF_TIME)
+ fputs("00:00:00 ", stdout); /* FIXME */
+ if (outflags & OF_CMD) {
+ char name[9];
+ strncpy(name, pp->p_name, 8);
+ name[8] = '\0';
+
+ for (j = 0; j < 8; ++j) {
+ if (name[j] != 0)
+ if (name[j] < ' ' || name[j] > 126)
+ name[j] = '?';
+ }
+ fputs(name, stdout);
+ if (pp->p_status == P_ZOMBIE)
+ fputs(" <defunct>", stdout);
+ putchar('\n');
}
}
struct p_tab_buffer *ppbuf;
struct p_tab *pp;
- uid = getuid();
+ uid = geteuid();
+ pid = getpid();
if ((pfd = open("/dev/proc", O_RDONLY)) < 0) {
perror("ps");
return 1;
}
ppid_slot[i] = ptab[i].p_tab.p_pptr - ptab[0].p_tab.p_pptr;
+ /* Learn our tty internal reference as we go */
+ if (ptab[i].p_tab.p_status && ptab[i].p_tab.p_pid == pid)
+ tty = ptab[i].p_tab.p_tty;
}
close(pfd);
return 0;
}
-int main(int argc, char *argv[])
+static void usage(void)
{
- int i;
+ fputs("usage: ps -[eNr]\n", stderr);
+ exit(1);
+}
- flags = 0;
- for (i = 1; i < argc; ++i) {
- char *p = argv[i];
+int main(int argc, char *argv[])
+{
+ int opt;
- if (*p == '-')
- ++p;
- while (*p)
- switch (*p++) {
+ optflags = 0;
+ outflags = OF_PID|OF_TTY|OF_TIME|OF_CMD;
+
+ while((opt = getopt(argc, argv, "AeaNrp:t:U:u:lf")) != -1) {
+ switch(opt) {
+ case 'A':
+ case 'e':
+ /* Show everything */
+ optflags |= F_e;
+ break;
case 'a':
- flags |= F_a;
+ /* All except session leaders and not
+ associated with tty. We only do not assoc */
+ optflags |= F_a;
break;
- case 'h':
- flags |= F_h;
+ case 'N':
+ /* Invert match */
+ optflags |= F_N;
break;
+ /* BSDism : keep if doesn't clash */
case 'r':
- flags |= F_r;
+ /* Only running processes */
+ optflags |= F_r;
+ break;
+ case 'p':
+ /* Must match this pid */
+ optflags |= F_p;
+ add_pidmatch(optarg);
+ break;
+ case 't':
+ optflags |= F_t;
+ add_ttymatch(optarg);
break;
- case 'n':
- flags |= F_n;
+ case 'U':
+ case 'u': /* -u deviates from the standard */
+ optflags |= F_u;
+ add_uidmatch(optarg);
+ break;
+ case 'l':
+ outflags |= OF_F|OF_S|OF_UID|OF_PID|OF_PPID|
+ OF_C|OF_PRI|OF_NI|OF_ADDR|OF_SZ|
+ OF_WCHAN|OF_TTY|OF_TIME|OF_CMD;
+ break;
+ case 'f':
+ outflags |= OF_UID|OF_PID|OF_PPID|OF_C|
+ OF_STIME|OF_TTY|OF_TIME|OF_CMD;
break;
default:
- fprintf(stderr, "usage: ps [-][ahrn]\n");
- return 1;
- }
+ usage();
+ }
}
+ if (optind != argc)
+ usage();
return do_ps();
}