ps: make it work like it's supposed to
authorAlan Cox <alan@linux.intel.com>
Sat, 7 Jul 2018 14:01:19 +0000 (15:01 +0100)
committerAlan Cox <alan@linux.intel.com>
Sat, 7 Jul 2018 14:01:19 +0000 (15:01 +0100)
This still needs work on times, but the kernel needs to catch up first!

Applications/util/ps.c

index fa5d88e..1501054 100644 (file)
@@ -1,20 +1,73 @@
+/*
+ *     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:
@@ -36,17 +89,209 @@ char mapstat(char s)
 }
 
 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)
@@ -54,39 +299,88 @@ 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');
        }
 }
 
@@ -96,7 +390,8 @@ int do_ps(void)
        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");
@@ -130,6 +425,9 @@ int do_ps(void)
                        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);
 
@@ -146,35 +444,69 @@ int do_ps(void)
        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();
 }