From 9494919ea5116075e16daac1355265b023419cdc Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 5 Nov 2006 00:45:47 +0000 Subject: ps: implement POSIX-like options, most notably -o (activated by CONFIG_DESKTOP) --- procps/ps.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) (limited to 'procps') diff --git a/procps/ps.c b/procps/ps.c index 2ff6e77d4..3cb86e49c 100644 --- a/procps/ps.c +++ b/procps/ps.c @@ -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 -- cgit v1.2.3