diff options
Diffstat (limited to 'utils/ps.c')
-rw-r--r-- | utils/ps.c | 472 |
1 files changed, 153 insertions, 319 deletions
@@ -1,8 +1,8 @@ /* Show process information. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2002,2006 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + Written by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -30,96 +30,78 @@ #include <idvec.h> #include <ps.h> #include <error.h> +#include <version.h> + #include "psout.h" +#include "parse.h" +#include "pids.h" -char *argp_program_version = "ps 1.0 (GNU " HURD_RELEASE ")"; +const char *argp_program_version = STANDARD_HURD_VERSION (ps); #define OA OPTION_ARG_OPTIONAL static const struct argp_option options[] = { - {"all-users", 'a', 0, 0, "List other users' processes"}, + {0,0,0,0, "Output format selection:", 1}, {"format", 'F', "FMT", 0, "Use the output-format FMT; FMT may be" " `default', `user', `vmem', `long'," " `jobc', `full', `hurd', `hurd-long'," " or a custom format-string"}, {"posix-format",'o', "FMT", 0, "Use the posix-style output-format FMT"}, + {0, 'f', 0, 0, "Use the `full' output-format"}, + {0, 'j', 0, 0, "Use the `jobc' output-format"}, + {0, 'l', 0, 0, "Use the `long' output-format"}, + {0, 'u', 0, 0, "Use the `user' output-format"}, + {0, 'v', 0, 0, "Use the `vmem' output-format"}, + + {0,0,0,0, "Process filtering (by default, other users'" + " processes, threads, and process-group leaders are not shown):", 2}, + {"all-users", 'a', 0, 0, "List other users' processes"}, {0, 'd', 0, 0, "List all processes except process group" " leaders"}, {"all", 'e', 0, 0, "List all processes"}, {0, 'A', 0, OPTION_ALIAS}, /* Posix option meaning -e */ - {0, 'f', 0, 0, "Use the `full' output-format"}, {0, 'g', 0, 0, "Include session and login leaders"}, - {"no-header", 'H', 0, 0, "Don't print a descriptive header line"}, - {0, 'j', 0, 0, "Use the `jobc' output-format"}, - {0, 'l', 0, 0, "Use the `long' output-format"}, - {"login", 'L', "LID", OA, "Add the processes from the login" - " collection LID (which defaults that of" - " the current process)"}, - {"lid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN}, + {"owner", 'U', "USER", 0, "Show only processes owned by USER"}, + {"not-owner", 'O', "USER", 0, "Show only processes not owned by USER"}, + {"no-parent", 'P', 0, 0, "Include processes without parents"}, + {"threads", 'T', 0, 0, "Show the threads for each process"}, + {"tty", 't', "TTY", OA, "Only show processes with controlling" + " terminal TTY"}, + {0, 'x', 0, 0, "Include orphaned processes"}, + + {0,0,0,0, "Elision of output fields:", 4}, {"no-msg-port",'M', 0, 0, "Don't show info that uses a process's" " msg port"}, {"nominal-fields",'n', 0, 0, "Don't elide fields containing" " `uninteresting' data"}, - {"owner", 'U', "USER", 0, "Show only processes owned by USER"}, - {"not-owner", 'O', "USER", 0, "Show only processes not owned by USER"}, - {"pid", 'p', "PID", 0, "List the process PID"}, - {"pgrp", 'G', "PGRP", 0, "List processes in process group PGRP"}, - {"no-parent", 'P', 0, 0, "Include processes without parents"}, {"all-fields", 'Q', 0, 0, "Don't elide unusable fields (normally" " if there's some reason ps can't print" " a field for any process, it's removed" " from the output entirely)"}, + + {0,0,0,0, "Output attributes:"}, + {"no-header", 'H', 0, 0, "Don't print a descriptive header line"}, {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, - {"session", 'S', "SID", OA, "Add the processes from the session SID" - " (which defaults to the sid of the" - " current process)"}, - {"sid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN}, - {"sort", 'S', "FIELD",0, "Sort the output with respect to FIELD," + {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD," " backwards if FIELD is prefixed by `-'"}, - {"threads", 'T', 0, 0, "Show the threads for each process"}, - {"tty", 't', "TTY", OA, "Only show processes with controlling" - " terminal TTY"}, - {0, 'u', 0, 0, "Use the `user' output-format"}, - {0, 'v', 0, 0, "Use the `vmem' output-format"}, + {"top", 'h', "ENTRIES", OA, "Show the top ENTRIES processes" + " (default 10), or if ENTRIES is" + " negative, the bottom -ENTRIES"}, + {"head", 0, 0, OPTION_ALIAS}, + {"bottom", 'b', "ENTRIES", OA, "Show the bottom ENTRIES processes" + " (default 10)"}, + {"tail", 0, 0, OPTION_ALIAS}, {"width", 'w', "WIDTH",OA, "If WIDTH is given, try to format the" " output for WIDTH columns, otherwise," - " remove the default limit"}, - {0, 'x', 0, 0, "Include orphaned processes"}, + " remove the default limit"}, {0, 0} }; -char *args_doc = "[PID...]"; - -char *doc = "The USER, LID, PID, PGRP, and SID arguments may also be comma \ -separated lists. The System V options -u and -g may be accessed with -O and \ ---pgrp."; - -int -parse_enum(char *arg, char **choices, char *kind, int allow_mismatches) -{ - int arglen = strlen(arg); - char **p = choices; - int partial_match = -1; - - while (*p != NULL) - if (strcmp(*p, arg) == 0) - return p - choices; - else - { - if (strncmp(*p, arg, arglen) == 0) - if (partial_match >= 0) - argp_error (0, "%s: Ambiguous %s", arg, kind); - else - partial_match = p - choices; - p++; - } - - if (partial_match < 0 && !allow_mismatches) - argp_error (0, "%s: Invalid %s", arg, kind); - - return partial_match; -} +static const char doc[] = +"Show information about processes PID... (default all `interesting' processes)" +"\vThe USER, LID, PID, PGRP, and SID arguments may also be comma separated" +" lists. The System V options -u and -g may be accessed with -O and -G."; #define FILTER_OWNER 0x01 #define FILTER_NOT_LEADER 0x02 @@ -127,38 +109,36 @@ parse_enum(char *arg, char **choices, char *kind, int allow_mismatches) #define FILTER_UNORPHANED 0x08 #define FILTER_PARENTED 0x10 -enum procsets +/* A particular predefined output format. */ +struct output_fmt { - PROCSET_ALL, PROCSET_SESSION, PROCSET_LOGIN + const char *name; + const char *sort_key; /* How this format should be sorted. */ + const char *fmt; /* The format string. */ }; -char *procset_names[] = -{"all", "session", "login", 0}; - -/* The names of various predefined output formats. */ -char *fmt_names[] = - {"default", "user", "vmem", "long", "jobc", "full", "hurd", "hurd-long",0}; -/* How each of those formats should be sorted; */ -char *fmt_sortkeys[] = - {"pid", "-cpu","-mem", "pid", "pid", "pid", "pid", "pid"}; -/* and the actual format strings. */ -char *fmts[] = + +/* The predefined output formats. */ +struct output_fmt output_fmts[] = { - /* default */ - "%^%?user %pid %th %tt %sc %stat %time %command", - /* user (-u) */ - "%^%user %pid %th %cpu %mem %sz %rss %tt %sc %stat %command", - /* vmem (-v) */ - "%^%pid %th %stat %sl %pgins %pgflts %cowflts %zfills %sz %rss %cpu %mem %command", - /* long (-l) */ - "%^%uid %pid %th %ppid %pri %ni %nth %msgi %msgo %sz %rss %sc %wait %stat %tt %time %command", - /* jobc (-j) */ - "%^%user %pid %th %ppid %pgrp %sess %lcoll %sc %stat %tt %time %command", - /* full (-f) (from sysv) */ - "%^%-user %pid %ppid %tty %time %command", - /* hurd */ - "%pid %th %uid %nth %{vsize:Vmem} %rss %{utime:User} %{stime:System} %args", - /* hurd-long */ - "%pid %th %uid %ppid %pgrp %sess %nth %{vsize:Vmem} %rss %cpu %{utime:User} %{stime:System} %args" + { "default", "pid", + "%^%?user %pid %th %tt %sc %stat %time %command" }, + { "user", "-cpu", + "%^%user %pid %th %cpu %mem %sz %rss %tt %sc %stat %start %time %command" }, + { "vmem", "-mem", + "%^%pid %th %stat %sl %pgins %pgflts %cowflts %zfills %sz %rss %cpu %mem %command" + }, + { "long", "pid", + "%^%uid %pid %th %ppid %pri %ni %nth %msgi %msgo %sz %rss %sc %wait %stat %tt %time %command" }, + { "jobc", "pid", + "%^%user %pid %th %ppid %pgrp %sess %lcoll %sc %stat %tt %time %command" }, + { "full", "pid", + "%^%-user %pid %ppid %tty %time %command" }, + { "hurd", "pid", + "%pid %th %uid %nth %{vsize:Vmem} %rss %{utime:User} %{stime:System} %args" + }, + { "hurd-long", "pid", + "%pid %th %uid %ppid %pgrp %sess %nth %{vsize:Vmem} %rss %cpu %{utime:User} %{stime:System} %args" + } }; /* Augment the standard specs with our own abbrevs. */ @@ -172,141 +152,17 @@ spec_abbrevs[] = { static struct ps_fmt_specs ps_specs = { spec_abbrevs, &ps_std_fmt_specs }; -/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is - empty and DEFAULT_ADD_FN isn't NULL, then call DEFAULT_ADD_FN instead. */ -static void -_parse_strlist (char *arg, - void (*add_fn)(const char *str), void (*default_add_fn)(), - const char *type_name) -{ - if (arg) - while (isspace(*arg)) - arg++; - - if (arg == NULL || *arg == '\0') - if (default_add_fn) - (*default_add_fn)(); - else - error(7, 0, "Empty %s list", type_name); - else - { - char *end = arg; - - void mark_end() - { - *end++ = '\0'; - while (isspace(*end)) - end++; - } - void parse_element() - { - if (*arg == '\0') - error(7, 0, "Empty element in %s list", type_name); - (*add_fn)(arg); - arg = end; - } - - while (*end != '\0') - switch (*end) - { - case ' ': case '\t': - mark_end(); - if (*end == ',') - mark_end(); - parse_element(); - break; - case ',': - mark_end(); - parse_element(); - break; - default: - end++; - } - - parse_element(); - } -} - -/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is - empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling - DEFAULT_FN instead, otherwise signal an error. */ -static void -parse_strlist (char *arg, - void (*add_fn)(const char *str), - const char *(*default_fn)(), - const char *type_name) -{ - void default_str_add() { (*add_fn)((*default_fn)()); } - _parse_strlist(arg, add_fn, default_str_add, type_name); -} - -/* For each numeric string in the comma-separated list in ARG, call ADD_FN; - if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number, - and call ADD_FN on that, otherwise signal an error. If any member of the - list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return - an integer for the string. LOOKUP_FN should signal an error itself it - there's some problem parsing the string. */ -static void -parse_numlist (char *arg, - void (*add_fn)(unsigned num), - int (*default_fn)(), - int (*lookup_fn)(const char *str), - const char *type_name) -{ - void default_num_add() { (*add_fn)((*default_fn)()); } - void add_num_str(const char *str) - { - const char *p; - for (p = str; *p != '\0'; p++) - if (!isdigit(*p)) - { - if (lookup_fn) - (*add_fn)((*lookup_fn)(str)); - else - error (7, 0, "%s: Invalid %s", p, type_name); - return; - } - (*add_fn)(atoi(str)); - } - _parse_strlist(arg, add_num_str, default_fn ? default_num_add : 0, - type_name); -} - -static process_t proc_server; - -/* Returns our session id. */ -static pid_t -current_sid() -{ - pid_t sid; - error_t err = proc_getsid(proc_server, getpid(), &sid); - if (err) - error(2, err, "Couldn't get current session id"); - return sid; -} - -/* Returns our login collection id. */ -static pid_t -current_lid() -{ - pid_t lid; - error_t err = proc_getloginid(proc_server, getpid(), &lid); - if (err) - error(2, err, "Couldn't get current login collection") ; - return lid; -} - /* Returns the UID for the user called NAME. */ static int -lookup_user(const char *name) +lookup_user (const char *name, struct argp_state *state) { struct passwd *pw = getpwnam(name); if (pw == NULL) - error(2, 0, "%s: Unknown user", name); + argp_failure (state, 2, 0, "%s: Unknown user", name); return pw->pw_uid; } -void +int main(int argc, char *argv[]) { error_t err; @@ -317,10 +173,10 @@ main(int argc, char *argv[]) char *arg_hack_buf = 0; struct idvec *only_uids = make_idvec (), *not_uids = make_idvec (); char *tty_names = 0; - unsigned num_tty_names = 0; + size_t num_tty_names = 0; struct proc_stat_list *procset; struct ps_context *context; - char *fmt_string = "default", *sort_key_name = NULL; + const char *fmt_string = "default", *sort_key_name = NULL; unsigned filter_mask = FILTER_OWNER | FILTER_NOT_LEADER | FILTER_UNORPHANED | FILTER_PARENTED; int sort_reverse = FALSE, print_heading = TRUE; @@ -329,65 +185,26 @@ main(int argc, char *argv[]) int output_width = -1; /* Desired max output size. */ int show_non_hurd_procs = 1; /* Show non-hurd processes. */ int posix_fmt = 0; /* Use a posix_fmt-style format string. */ + int top = 0; /* Number of entries to output. */ + pid_t *pids = 0; /* User-specified pids. */ + size_t num_pids = 0; + struct pids_argp_params pids_argp_params = { &pids, &num_pids, 1 }; - /* Add a specific process to be printed out. */ - void add_pid (unsigned pid) - { - struct proc_stat *ps; - - err = proc_stat_list_add_pid (procset, pid, &ps); - if (err) - error (0, err, "%d: Can't add process", pid); - - /* See if this process actually exists. */ - proc_stat_set_flags (ps, PSTAT_PROC_INFO); - if (! proc_stat_has (ps, PSTAT_PROC_INFO)) - /* Give an error message; using ps_alive_filter below will delete the - entry so it doesn't get output. */ - error (0, 0, "%d: Unknown process", pid); - - /* If explicit processes are specified, we probably don't want to - filter them out later. This implicit turning off of filtering might - be confusing in the case where a login-collection or session is - specified along with some pids, but it's probably not worth worrying - about. */ - filter_mask = 0; - } - /* Print out all process from the given session. */ - void add_sid(unsigned sid) - { - err = proc_stat_list_add_session (procset, sid, 0, 0); - if (err) - error(2, err, "%u: Can't add session", sid); - } - /* Print out all process from the given login collection. */ - void add_lid(unsigned lid) - { - error_t err = proc_stat_list_add_login_coll (procset, lid, 0, 0); - if (err) - error(2, err, "%u: Can't add login collection", lid); - } - /* Print out all process from the given process group. */ - void add_pgrp(unsigned pgrp) - { - error_t err = proc_stat_list_add_pgrp (procset, pgrp, 0, 0); - if (err) - error(2, err, "%u: Can't add process group", pgrp); - } - /* Add a user who's processes should be printed out. */ - void add_uid (uid_t uid) + error_t add_uid (uid_t uid, struct argp_state *state) { error_t err = idvec_add (only_uids, uid); if (err) - error (23, err, "Can't add uid"); + argp_failure (state, 23, err, "Can't add uid"); + return err; } /* Add a user who's processes should not be printed out. */ - void add_not_uid (uid_t uid) + error_t add_not_uid (uid_t uid, struct argp_state *state) { error_t err = idvec_add (not_uids, uid); if (err) - error (23, err, "Can't add uid"); + argp_failure (state, 23, err, "Can't add uid"); + return err; } /* Returns TRUE if PS is owned by any of the users in ONLY_UIDS, and none in NOT_UIDS. */ @@ -403,11 +220,12 @@ main(int argc, char *argv[]) /* Add TTY_NAME to the list for which processes with those controlling terminals will be printed. */ - void add_tty_name (const char *tty_name) + error_t add_tty_name (const char *tty_name, struct argp_state *state) { error_t err = argz_add (&tty_names, &num_tty_names, tty_name); if (err) - error (8, err, "%s: Can't add tty", tty_name); + argp_failure (state, 8, err, "%s: Can't add tty", tty_name); + return err; } int proc_stat_has_ctty(struct proc_stat *ps) { @@ -431,7 +249,7 @@ main(int argc, char *argv[]) } /* Returns the name of the current controlling terminal. */ - static const char *current_tty_name() + const char *current_tty_name() { error_t err; struct ps_tty *tty; @@ -463,10 +281,9 @@ main(int argc, char *argv[]) memcpy (&state->argv[state->next][1], arg, len); break; } - /* Otherwise, fall through and treat the arg as a process id. */ - case 'p': - parse_numlist(arg, add_pid, NULL, NULL, "process id"); - break; + else + /* Let PIDS_ARGP handle it. */ + return ARGP_ERR_UNKNOWN; case 'a': filter_mask &= ~FILTER_OWNER; break; case 'd': filter_mask &= ~(FILTER_OWNER | FILTER_UNORPHANED); break; @@ -486,6 +303,8 @@ main(int argc, char *argv[]) case 'T': show_threads = TRUE; break; case 's': sort_key_name = arg; break; case 'r': sort_reverse = TRUE; break; + case 'h': top = arg ? atoi (arg) : 10; break; + case 'b': top = -(arg ? atoi (arg) : 10); break; case 'F': fmt_string = arg; posix_fmt = 0; break; case 'o': fmt_string = arg; posix_fmt = 1; break; @@ -494,22 +313,37 @@ main(int argc, char *argv[]) break; case 't': - parse_strlist (arg, add_tty_name, current_tty_name, "tty"); - break; + return parse_strlist (arg, add_tty_name, current_tty_name, "tty", state); case 'U': - parse_numlist (arg, add_uid, NULL, lookup_user, "user"); - break; + return parse_numlist (arg, add_uid, NULL, lookup_user, "user", state); case 'O': - parse_numlist (arg, add_not_uid, NULL, lookup_user, "user"); - break; - case 'S': - parse_numlist(arg, add_sid, current_sid, NULL, "session id"); - break; - case 'L': - parse_numlist(arg, add_lid, current_lid, NULL, "login collection"); + return parse_numlist (arg, add_not_uid, NULL, lookup_user, "user", state); + + case ARGP_KEY_INIT: + /* Initialize inputs for child parsers. */ + state->child_inputs[0] = &pids_argp_params; break; - case 'G': - parse_numlist(arg, add_pgrp, NULL, NULL, "process group"); + + case ARGP_KEY_SUCCESS: + /* Select an explicit format string if FMT_STRING is a format + name. This is done here because parse_enum needs STATE. */ + { + const char *fmt_name (unsigned n) + { + return + n >= (sizeof output_fmts / sizeof *output_fmts) + ? 0 + : output_fmts[n].name; + } + int fmt_index = parse_enum (fmt_string, fmt_name, + "format type", 1, state); + if (fmt_index >= 0) + { + fmt_string = output_fmts[fmt_index].fmt; + if (sort_key_name == NULL) + sort_key_name = output_fmts[fmt_index].sort_key; + } + } break; default: @@ -518,46 +352,27 @@ main(int argc, char *argv[]) return 0; } - struct argp argp = { options, parse_opt, args_doc, doc}; - - proc_server = getproc(); + struct argp_child argp_kids[] = + { { &pids_argp, 0, + "Process selection (before filtering; default is all processes):", 3}, + {0} }; + struct argp argp = { options, parse_opt, 0, doc, argp_kids }; - err = ps_context_create (proc_server, &context); + err = ps_context_create (getproc (), &context); if (err) error(1, err, "ps_context_create"); - err = proc_stat_list_create(context, &procset); - if (err) - error(1, err, "proc_stat_list_create"); - /* Parse our command line. This shouldn't ever return an error. */ argp_parse (&argp, argc, argv, 0, 0, 0); - if (only_uids->num == 0 && (filter_mask & FILTER_OWNER)) - /* Restrict the output to only our own processes. */ - { - int uid = getuid (); - if (uid >= 0) - add_uid (uid); - else - filter_mask &= ~FILTER_OWNER; /* Must be an anonymous process. */ - } - - { - int fmt_index = parse_enum(fmt_string, fmt_names, "format type", 1); - if (fmt_index >= 0) - { - fmt_string = fmts[fmt_index]; - if (sort_key_name == NULL) - sort_key_name = fmt_sortkeys[fmt_index]; - } - } + err = proc_stat_list_create(context, &procset); + if (err) + error(1, err, "proc_stat_list_create"); - if (proc_stat_list_num_procs (procset) == 0) + if (num_pids == 0) + /* No explicit processes specified. */ { err = proc_stat_list_add_all (procset, 0, 0); - if (err) - error(2, err, "Can't get process list"); /* Try to avoid showing non-hurd processes if this isn't a native-booted hurd system (because there would be lots of them). Here we use a @@ -576,10 +391,29 @@ main(int argc, char *argv[]) show_non_hurd_procs = 1; } } + else + /* User-specified processes. */ + { + err = proc_stat_list_add_pids (procset, pids, num_pids, 0); + filter_mask = 0; /* Don't mess with them. */ + } + + if (err) + error(2, err, "Can't get process list"); if (no_msg_port) proc_stat_list_set_flags(procset, PSTAT_NO_MSGPORT); + if (only_uids->num == 0 && (filter_mask & FILTER_OWNER)) + /* Restrict the output to only our own processes. */ + { + int uid = getuid (); + if (uid >= 0) + add_uid (uid, 0); + else + filter_mask &= ~FILTER_OWNER; /* Must be an anonymous process. */ + } + /* Filter out any processes that we don't want to show. */ if (only_uids->num || not_uids->num) proc_stat_list_filter1 (procset, proc_stat_owner_ok, @@ -606,7 +440,7 @@ main(int argc, char *argv[]) psout (procset, fmt_string, posix_fmt, &ps_specs, sort_key_name, sort_reverse, output_width, print_heading, - squash_bogus_fields, squash_nominal_fields); + squash_bogus_fields, squash_nominal_fields, top); - exit (0); + return 0; } |