ps: implement POSIX-like options, most notably -o
(activated by CONFIG_DESKTOP)
This commit is contained in:
parent
aae0311356
commit
9494919ea5
@ -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 \
|
||||
|
275
procps/ps.c
275
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
|
||||
|
Loading…
Reference in New Issue
Block a user