From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- release/src/router/busybox/procps/ps.c | 623 ++++++++++++++++++++++++++++----- 1 file changed, 538 insertions(+), 85 deletions(-) (limited to 'release/src/router/busybox/procps/ps.c') diff --git a/release/src/router/busybox/procps/ps.c b/release/src/router/busybox/procps/ps.c index 9dc45d35..395cfcf5 100644 --- a/release/src/router/busybox/procps/ps.c +++ b/release/src/router/busybox/procps/ps.c @@ -2,116 +2,569 @@ /* * Mini ps implementation(s) for busybox * - * Copyright (C) 1999-2003 by Erik Andersen + * Copyright (C) 1999-2004 by Erik Andersen + * Fix for SELinux Support:(c)2007 Hiroshi Shinji + * (c)2007 Yuichi Nakamura * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place, Suite 330, Boston, MA 02111-1307 USA + * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "busybox.h" -#ifdef CONFIG_SELINUX -#include -#include -#include /* for is_flask_enabled() */ +#include "libbb.h" + +/* Absolute maximum on output line length */ +enum { MAX_WIDTH = 2*1024 }; + +#if ENABLE_DESKTOP + +#include /* for times() */ +//#include /* for sysinfo() */ +#ifndef AT_CLKTCK +#define AT_CLKTCK 17 #endif -static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ +#if ENABLE_SELINUX +#define SELINUX_O_PREFIX "label," +#define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" USE_FEATURE_PS_TIME(",time") ",args") +#else +#define DEFAULT_O_STR ("pid,user" USE_FEATURE_PS_TIME(",time") ",args") +#endif +typedef struct { + uint16_t width; + char name[6]; + const char *header; + void (*f)(char *buf, int size, const procps_status_t *ps); + int ps_flags; +} ps_out_t; -extern int ps_main(int argc, char **argv) +struct globals { + ps_out_t* out; + int out_cnt; + int print_header; + int need_flags; + char *buffer; + unsigned terminal_width; +#if ENABLE_FEATURE_PS_TIME + unsigned kernel_HZ; + unsigned long long seconds_since_boot; +#endif + char default_o[sizeof(DEFAULT_O_STR)]; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define out (G.out ) +#define out_cnt (G.out_cnt ) +#define print_header (G.print_header ) +#define need_flags (G.need_flags ) +#define buffer (G.buffer ) +#define terminal_width (G.terminal_width ) +#define kernel_HZ (G.kernel_HZ ) +#define seconds_since_boot (G.seconds_since_boot) +#define default_o (G.default_o ) + +#if ENABLE_FEATURE_PS_TIME +/* for ELF executables, notes are pushed before environment and args */ +static ptrdiff_t find_elf_note(ptrdiff_t findme) { - procps_status_t * p; - int i, len; -#ifdef CONFIG_FEATURE_AUTOWIDTH - struct winsize win = { 0, 0, 0, 0 }; - int terminal_width = TERMINAL_WIDTH; + ptrdiff_t *ep = (ptrdiff_t *) environ; + + while (*ep++); + while (*ep) { + if (ep[0] == findme) { + return ep[1]; + } + ep += 2; + } + return -1; +} + +#if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS +static unsigned get_HZ_by_waiting(void) +{ + struct timeval tv1, tv2; + unsigned t1, t2, r, hz; + unsigned cnt = cnt; /* for compiler */ + int diff; + + r = 0; + + /* Wait for times() to reach new tick */ + t1 = times(NULL); + do { + t2 = times(NULL); + } while (t2 == t1); + gettimeofday(&tv2, NULL); + + do { + t1 = t2; + tv1.tv_usec = tv2.tv_usec; + + /* Wait exactly one times() tick */ + do { + t2 = times(NULL); + } while (t2 == t1); + gettimeofday(&tv2, NULL); + + /* Calculate ticks per sec, rounding up to even */ + diff = tv2.tv_usec - tv1.tv_usec; + if (diff <= 0) diff += 1000000; + hz = 1000000u / (unsigned)diff; + hz = (hz+1) & ~1; + + /* Count how many same hz values we saw */ + if (r != hz) { + r = hz; + cnt = 0; + } + cnt++; + } while (cnt < 3); /* exit if saw 3 same values */ + + return r; +} #else -#define terminal_width TERMINAL_WIDTH +static inline unsigned get_HZ_by_waiting(void) +{ + /* Better method? */ + return 100; +} #endif -#ifdef CONFIG_SELINUX - int use_selinux = 0; - security_id_t sid; - if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") ) - use_selinux = 1; -#endif +static unsigned get_kernel_HZ(void) +{ + //char buf[64]; + struct sysinfo info; + + if (kernel_HZ) + return kernel_HZ; + + /* Works for ELF only, Linux 2.4.0+ */ + kernel_HZ = find_elf_note(AT_CLKTCK); + if (kernel_HZ == (unsigned)-1) + kernel_HZ = get_HZ_by_waiting(); + //if (open_read_close("/proc/uptime", buf, sizeof(buf) <= 0) + // bb_perror_msg_and_die("cannot read %s", "/proc/uptime"); + //buf[sizeof(buf)-1] = '\0'; + ///sscanf(buf, "%llu", &seconds_since_boot); + sysinfo(&info); + seconds_since_boot = info.uptime; -#ifdef CONFIG_FEATURE_AUTOWIDTH - ioctl(fileno(stdout), TIOCGWINSZ, &win); - if (win.ws_col > 0) - terminal_width = win.ws_col - 1; + return kernel_HZ; +} #endif -#ifdef CONFIG_SELINUX - if(use_selinux) - printf(" PID Context Stat Command\n"); +/* Print value to buf, max size+1 chars (including trailing '\0') */ + +static void func_user(char *buf, int size, const procps_status_t *ps) +{ +#if 1 + safe_strncpy(buf, get_cached_username(ps->uid), size+1); +#else + /* "compatible" version, but it's larger */ + /* procps 2.18 shows numeric UID if name overflows the field */ + /* TODO: get_cached_username() returns numeric string if + * user has no passwd record, we will display it + * left-justified here; too long usernames are shown + * as _right-justified_ IDs. Is it worth fixing? */ + const char *user = get_cached_username(ps->uid); + if (strlen(user) <= size) + safe_strncpy(buf, user, size+1); else + sprintf(buf, "%*u", size, (unsigned)ps->uid); #endif - printf(" PID Uid VmSize Stat Command\n"); -#ifdef CONFIG_SELINUX - while ((p = procps_scan(1, use_selinux, &sid)) != 0) { -#else - while ((p = procps_scan(1)) != 0) { +} + +static void func_comm(char *buf, int size, const procps_status_t *ps) +{ + safe_strncpy(buf, ps->comm, size+1); +} + +static void func_args(char *buf, int size, const procps_status_t *ps) +{ + read_cmdline(buf, size, ps->pid, ps->comm); +} + +static void func_pid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->pid); +} + +static void func_ppid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->ppid); +} + +static void func_pgid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->pgid); +} + +static void put_lu(char *buf, int size, unsigned long u) +{ + char buf4[5]; + + /* see http://en.wikipedia.org/wiki/Tera */ + smart_ulltoa4(u, buf4, " mgtpezy"); + buf4[4] = '\0'; + sprintf(buf, "%.*s", size, buf4); +} + +static void func_vsz(char *buf, int size, const procps_status_t *ps) +{ + put_lu(buf, size, ps->vsz); +} + +static void func_rss(char *buf, int size, const procps_status_t *ps) +{ + put_lu(buf, size, ps->rss); +} + +static void func_tty(char *buf, int size, const procps_status_t *ps) +{ + buf[0] = '?'; + buf[1] = '\0'; + if (ps->tty_major) /* tty field of "0" means "no tty" */ + snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); +} + +#if ENABLE_FEATURE_PS_TIME +static void func_etime(char *buf, int size, const procps_status_t *ps) +{ + /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */ + unsigned long mm; + unsigned ss; + + mm = ps->start_time / get_kernel_HZ(); + /* must be after get_kernel_HZ()! */ + mm = seconds_since_boot - mm; + ss = mm % 60; + mm /= 60; + snprintf(buf, size+1, "%3lu:%02u", mm, ss); +} + +static void func_time(char *buf, int size, const procps_status_t *ps) +{ + /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */ + unsigned long mm; + unsigned ss; + + mm = (ps->utime + ps->stime) / get_kernel_HZ(); + ss = mm % 60; + mm /= 60; + snprintf(buf, size+1, "%3lu:%02u", mm, ss); +} +#endif + +#if ENABLE_SELINUX +static void func_label(char *buf, int size, const procps_status_t *ps) +{ + safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1); +} #endif - char *namecmd = p->cmd; -#ifdef CONFIG_SELINUX - if(use_selinux) +/* +static void func_nice(char *buf, int size, const procps_status_t *ps) +{ + ps->??? +} + +static void func_pcpu(char *buf, int size, const procps_status_t *ps) +{ +} +*/ + +static const ps_out_t out_spec[] = { +// Mandated by POSIX: + { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, + { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, + { 256 , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, + { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, + { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, + { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, +#if ENABLE_FEATURE_PS_TIME + { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, +#endif +// { sizeof("GROUP" )-1, "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, +// { sizeof("NI" )-1, "nice" ,"NI" ,func_nice ,PSSCAN_ }, +// { sizeof("%CPU" )-1, "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ }, +// { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID }, +// { sizeof("RUSER" )-1, "ruser" ,"RUSER" ,func_ruser ,PSSCAN_UIDGID }, +#if ENABLE_FEATURE_PS_TIME + { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, +#endif + { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, + { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, +// Not mandated by POSIX, but useful: + { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, +#if ENABLE_SELINUX + { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, +#endif +}; + +static ps_out_t* new_out_t(void) +{ + out = xrealloc_vector(out, 2, out_cnt); + return &out[out_cnt++]; +} + +static const ps_out_t* find_out_spec(const char *name) +{ + unsigned i; + for (i = 0; i < ARRAY_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; + } + // opt points to last spec in comma separated list. + // This one can have =HEADER part. + 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++) { + need_flags |= out[i].ps_flags; + if (out[i].header[0]) { + print_header = 1; + } + width += out[i].width + 1; /* "FIELD " */ + } +#if ENABLE_SELINUX + if (!is_selinux_enabled()) + need_flags &= ~PSSCAN_CONTEXT; +#endif + buffer = xmalloc(width + 1); /* for trailing \0 */ +} + +static void format_header(void) +{ + int i; + ps_out_t* op; + char *p; + + if (!print_header) + return; + p = buffer; + 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[0]) { + p[0] = '-'; + p[1] = '\0'; + } + len = strlen(p); + p += len; + len = out[i].width - len + 1; + if (++i == out_cnt) /* do not pad last field */ + break; + p += sprintf(p, "%*s", len, ""); + } + printf("%.*s\n", terminal_width, buffer); +} + +int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ps_main(int argc UNUSED_PARAM, char **argv) +{ + procps_status_t *p; + llist_t* opt_o = NULL; + USE_SELINUX(int opt;) + + // 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 display + /* We allow (and ignore) most of the above. FIXME */ + opt_complementary = "o::"; + USE_SELINUX(opt =) getopt32(argv, "Zo:aAdefl", &opt_o); + if (opt_o) { + do { + parse_o(llist_pop(&opt_o)); + } while (opt_o); + } else { + /* Below: parse_o() needs char*, NOT const char*... */ +#if ENABLE_SELINUX + if (!(opt & 1) || !is_selinux_enabled()) { + /* no -Z or no SELinux: do not show LABEL */ + strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1); + } else +#endif { - char sbuf[128]; - len = sizeof(sbuf); - if(security_sid_to_context(sid, (security_context_t)&sbuf, &len)) - strcpy(sbuf, "unknown"); + strcpy(default_o, DEFAULT_O_STR); + } + parse_o(default_o); + } + post_process(); + + /* Was INT_MAX, but some libc's go belly up with printf("%.*s") + * and such large widths */ + terminal_width = MAX_WIDTH; + if (isatty(1)) { + get_terminal_width_height(0, &terminal_width, NULL); + if (--terminal_width > MAX_WIDTH) + terminal_width = MAX_WIDTH; + } + format_header(); - len = printf("%5d %-32s %s ", p->pid, sbuf, p->state); + p = NULL; + while ((p = procps_scan(p, need_flags))) { + format_process(p); + } + + return EXIT_SUCCESS; +} + + +#else /* !ENABLE_DESKTOP */ + + +int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + procps_status_t *p = NULL; + int len; + SKIP_SELINUX(const) int use_selinux = 0; + USE_SELINUX(int i;) +#if !ENABLE_FEATURE_PS_WIDE + enum { terminal_width = 79 }; +#else + unsigned terminal_width; + int w_count = 0; +#endif + +#if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX +#if ENABLE_FEATURE_PS_WIDE + opt_complementary = "-:ww"; + USE_SELINUX(i =) getopt32(argv, USE_SELINUX("Z") "w", &w_count); + /* if w is given once, GNU ps sets the width to 132, + * if w is given more than once, it is "unlimited" + */ + if (w_count) { + terminal_width = (w_count==1) ? 132 : MAX_WIDTH; + } else { + get_terminal_width_height(0, &terminal_width, NULL); + /* Go one less... */ + if (--terminal_width > MAX_WIDTH) + terminal_width = MAX_WIDTH; + } +#else /* only ENABLE_SELINUX */ + i = getopt32(argv, "Z"); +#endif +#if ENABLE_SELINUX + if ((i & 1) && is_selinux_enabled()) + use_selinux = PSSCAN_CONTEXT; +#endif +#endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */ + + if (use_selinux) + puts(" PID CONTEXT STAT COMMAND"); + else + puts(" PID USER VSZ STAT COMMAND"); + + while ((p = procps_scan(p, 0 + | PSSCAN_PID + | PSSCAN_UIDGID + | PSSCAN_STATE + | PSSCAN_VSZ + | PSSCAN_COMM + | use_selinux + ))) { +#if ENABLE_SELINUX + if (use_selinux) { + len = printf("%5u %-32.32s %s ", + p->pid, + p->context ? p->context : "unknown", + p->state); + } else +#endif + { + const char *user = get_cached_username(p->uid); + //if (p->vsz == 0) + // len = printf("%5u %-8.8s %s ", + // p->pid, user, p->state); + //else + { + char buf6[6]; + smart_ulltoa5(p->vsz, buf6, " mgtpezy"); + buf6[5] = '\0'; + len = printf("%5u %-8.8s %s %s ", + p->pid, user, buf6, p->state); + } } - else -#endif - if(p->rss == 0) - len = printf("%5d %-8s %s ", p->pid, p->user, p->state); - else - len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state); - i = terminal_width-len; - - if(namecmd != 0 && namecmd[0] != 0) { - if(i < 0) - i = 0; - if(strlen(namecmd) > i) - namecmd[i] = 0; - printf("%s\n", namecmd); - } else { - namecmd = p->short_cmd; - if(i < 2) - i = 2; - if(strlen(namecmd) > (i-2)) - namecmd[i-2] = 0; - printf("[%s]\n", namecmd); + + { + int sz = terminal_width - len; + char buf[sz + 1]; + read_cmdline(buf, sz, p->pid, p->comm); + puts(buf); } - free(p->cmd); } + if (ENABLE_FEATURE_CLEAN_UP) + clear_username_cache(); return EXIT_SUCCESS; } +#endif /* ENABLE_DESKTOP */ -- cgit v1.2.3-54-g00ecf