ps: implement POSIX-like options, most notably -o
authorDenis Vlasenko <vda.linux@googlemail.com>
Sun, 5 Nov 2006 00:45:47 +0000 (00:45 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sun, 5 Nov 2006 00:45:47 +0000 (00:45 -0000)
(activated by CONFIG_DESKTOP)

include/usage.h
procps/ps.c

index 46bb9eca4ac6ece38aa6aa8e06f2bccadaecc0ba..7174ad414543d7fd4467f7930a556f8a40188b9a 100644 (file)
@@ -2377,6 +2377,18 @@ USE_FEATURE_MDEV_CONFIG( \
        "$ printf \"Val=%d\\n\" 5\n" \
        "Val=5\n"
 
+
+#if ENABLE_DESKTOP
+
+#define ps_trivial_usage \
+       ""
+#define ps_full_usage \
+       "Report process status\n" \
+       "\nOptions:" \
+       "\n\t-o col1,col2=header\tSelect columns for display" \
+
+#else /* !ENABLE_DESKTOP */
+
 #if !defined CONFIG_SELINUX && !ENABLE_FEATURE_PS_WIDE
 #define USAGE_PS "\n\tThis version of ps accepts no options."
 #else
@@ -2396,6 +2408,8 @@ USE_FEATURE_MDEV_CONFIG( \
        USE_SELINUX("\n\t-c\tshow SE Linux context") \
        USAGE_PS_WIDE("\n\tw\twide output")
 
+#endif /* ENABLE_DESKTOP */
+
 #define ps_example_usage \
        "$ ps\n" \
        "  PID  Uid      Gid State Command\n" \
@@ -2409,6 +2423,7 @@ USE_FEATURE_MDEV_CONFIG( \
        "  745 root     root     S [getty]\n" \
        " 2990 andersen andersen R ps\n"
 
+
 #define pwd_trivial_usage \
        ""
 #define pwd_full_usage \
index 2ff6e77d46f97257e67b2c20d51de52900e3250e..3cb86e49c4aba3f2203c017b9e9dfdf28f1644a8 100644 (file)
@@ -9,6 +9,279 @@
 
 #include "busybox.h"
 
+#if ENABLE_DESKTOP
+
+/* Print value to buf, max size+1 chars (including trailing '\0') */
+
+void func_user(char *buf, int size, const procps_status_t *ps)
+{
+       safe_strncpy(buf, get_cached_username(ps->uid), size+1);
+}
+
+void func_comm(char *buf, int size, const procps_status_t *ps)
+{
+       safe_strncpy(buf, ps->comm, size+1);
+}
+
+void func_args(char *buf, int size, const procps_status_t *ps)
+{
+       buf[0] = '\0';
+       if (ps->cmd)
+               safe_strncpy(buf, ps->cmd, size+1);
+       else if (size >= 2)
+               snprintf(buf, size+1, "[%.*s]", size-2, ps->comm);
+}
+
+void func_pid(char *buf, int size, const procps_status_t *ps)
+{
+       snprintf(buf, size+1, "%*u", size, ps->pid);
+}
+
+void func_ppid(char *buf, int size, const procps_status_t *ps)
+{
+       snprintf(buf, size+1, "%*u", size, ps->ppid);
+}
+
+void func_pgid(char *buf, int size, const procps_status_t *ps)
+{
+       snprintf(buf, size+1, "%*u", size, ps->pgid);
+}
+
+void func_rss(char *buf, int size, const procps_status_t *ps)
+{
+       char buf5[5];
+       smart_ulltoa5( ((unsigned long long)ps->rss) << 10, buf5);
+       snprintf(buf, size+1, "%.*s", size, buf5);
+}
+
+/*
+void func_nice(char *buf, int size, const procps_status_t *ps)
+{
+       ps->???
+}
+
+void func_etime(char *buf, int size, const procps_status_t *ps)
+{
+       elapled time [[dd-]hh:]mm:ss
+}
+
+void func_time(char *buf, int size, const procps_status_t *ps)
+{
+       cumulative time [[dd-]hh:]mm:ss
+}
+
+void func_pcpu(char *buf, int size, const procps_status_t *ps)
+{
+}
+
+void func_tty(char *buf, int size, const procps_status_t *ps)
+{
+}
+*/
+
+typedef struct {
+       char name[8];
+       const char *header;
+       void (*f)(char *buf, int size, const procps_status_t *ps);
+       int ps_flags;
+       int width;
+} ps_out_t;
+
+static const ps_out_t out_spec[] = {
+// Mandated by POSIX:
+       { "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID,8                   },
+       { "comm"  ,"COMMAND",func_comm  ,PSSCAN_COMM  ,16                  },
+       { "args"  ,"COMMAND",func_args  ,PSSCAN_CMD|PSSCAN_COMM,256        },
+       { "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID   ,5                   },
+       { "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID  ,5                   },
+       { "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID  ,5                   },
+//     { "etime" ,"ELAPSED",func_etime ,PSSCAN_      ,sizeof("ELAPSED")-1 },
+//     { "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID,sizeof("GROUP"  )-1 },
+//     { "nice"  ,"NI"     ,func_nice  ,PSSCAN_      ,sizeof("NI"     )-1 },
+//     { "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_      ,sizeof("%CPU"   )-1 },
+//     { "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID,sizeof("RGROUP" )-1 },
+//     { "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_UIDGID,sizeof("RUSER"  )-1 },
+//     { "time"  ,"TIME"   ,func_time  ,PSSCAN_      ,sizeof("TIME"   )-1 },
+//     { "tty"   ,"TT"     ,func_tty   ,PSSCAN_      ,sizeof("TT"     )-1 },
+//     { "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ   ,4                   },
+// Not mandated by POSIX:
+       { "rss"   ,"RSS"    ,func_rss   ,PSSCAN_RSS   ,4                   },
+};
+
+#define VEC_SIZE(v) ( sizeof(v) / sizeof((v)[0]) )
+
+static ps_out_t* out;
+static int out_cnt;
+static int print_header;
+static int ps_flags;
+static char *buffer;
+static unsigned terminal_width;
+
+
+static ps_out_t* new_out_t(void)
+{
+       int i = out_cnt++;
+       out = xrealloc(out, out_cnt * sizeof(*out));
+       return &out[i];
+}
+
+static const ps_out_t* find_out_spec(const char *name)
+{
+       int i;
+       for (i = 0; i < VEC_SIZE(out_spec); i++) {
+               if (!strcmp(name, out_spec[i].name))
+                       return &out_spec[i];
+       }
+       bb_error_msg_and_die("bad -o argument '%s'", name);
+}
+
+static void parse_o(char* opt)
+{
+       ps_out_t* new;
+       // POSIX: "-o is blank- or comma-separated list" (FIXME)
+       char *comma, *equal;
+       while (1) {
+               comma = strchr(opt, ',');
+               equal = strchr(opt, '=');
+               if (comma && (!equal || equal > comma)) {
+                       *comma = '\0';
+                       *new_out_t() = *find_out_spec(opt);
+                       *comma = ',';
+                       opt = comma + 1;
+                       continue;
+               }
+               break;
+       }
+       new = new_out_t();
+       if (equal)
+               *equal = '\0';
+       *new = *find_out_spec(opt);
+       if (equal) {
+               *equal = '=';
+               new->header = equal + 1;
+               // POSIX: the field widths shall be ... at least as wide as
+               // the header text (default or overridden value).
+               // If the header text is null, such as -o user=,
+               // the field width shall be at least as wide as the
+               // default header text
+               if (new->header[0]) {
+                       new->width = strlen(new->header);
+                       print_header = 1;
+               }
+       } else
+               print_header = 1;
+}
+
+static void post_process(void)
+{
+       int i;
+       int width = 0;
+       for (i = 0; i < out_cnt; i++) {
+               ps_flags |= out[i].ps_flags;
+               if (out[i].header[0]) {
+                       print_header = 1;
+               }
+               width += out[i].width + 1; /* "FIELD " */
+       }
+       buffer = xmalloc(width + 1); /* for trailing \0 */
+}
+
+static void format_header(void)
+{
+       int i;
+       ps_out_t* op;
+       char *p = buffer;
+       if (!print_header)
+               return;
+       i = 0;
+       if (out_cnt) {
+               while (1) {
+                       op = &out[i];
+                       if (++i == out_cnt) /* do not pad last field */
+                               break;
+                       p += sprintf(p, "%-*s ", op->width, op->header);
+               }
+               strcpy(p, op->header);
+       }
+       printf("%.*s\n", terminal_width, buffer);
+}
+
+static void format_process(const procps_status_t *ps)
+{
+       int i, len;
+       char *p = buffer;
+       i = 0;
+       if (out_cnt) while (1) {
+               out[i].f(p, out[i].width, ps);
+               // POSIX: Any field need not be meaningful in all
+               // implementations. In such a case a hyphen ( '-' )
+               // should be output in place of the field value.
+               if (!*p) {
+                       *p++ = '-';
+                       *p = '\0';
+               }
+               len = strlen(p);
+               p += len;
+               len = out[i].width - len + 1;
+               if (++i == out_cnt) /* do not pad last field */
+                       break;
+               while (len--)
+                       *p++ = ' ';
+               *p = '\0';
+       }
+       printf("%.*s\n", terminal_width, buffer);
+}
+
+/* Cannot be const: parse_o() will choke */
+static char default_o[] = "pid,user" /* TODO: ,vsz,stat */ ",args";
+
+int ps_main(int argc, char **argv)
+{
+       procps_status_t *p;
+       llist_t* opt_o = NULL;
+
+       // POSIX:
+       // -a  Write information for all processes associated with terminals
+       //     Implementations may omit session leaders from this list
+       // -A  Write information for all processes
+       // -d  Write information for all processes, except session leaders
+       // -e  Write information for all processes (equivalent to -A.)
+       // -f  Generate a full listing
+       // -l  Generate a long listing
+       // -o col1,col2,col3=header
+       //     Select which columns to distplay
+       /* We allow (and ignore) most of the above. FIXME */
+       opt_complementary = "o::";
+       getopt32(argc, argv, "o:aAdefl", &opt_o);
+       if (opt_o) {
+               opt_o = rev_llist(opt_o);
+               do {
+                       parse_o(opt_o->data);
+                       opt_o = opt_o->link;
+               } while (opt_o);
+       } else
+               parse_o(default_o);
+       post_process();
+
+       terminal_width = INT_MAX;
+       if (isatty(1)) {
+               get_terminal_width_height(1, &terminal_width, NULL);
+               terminal_width--;
+       }
+       format_header();
+
+       p = NULL;
+       while ((p = procps_scan(p, ps_flags))) {
+               format_process(p);
+       }
+
+       return EXIT_SUCCESS;
+}
+
+
+#else /* !ENABLE_DESKTOP */
+
+
 int ps_main(int argc, char **argv)
 {
        procps_status_t *p = NULL;
@@ -111,3 +384,5 @@ int ps_main(int argc, char **argv)
                clear_username_cache();
        return EXIT_SUCCESS;
 }
+
+#endif