aboutsummaryrefslogtreecommitdiff
path: root/utils/ps.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ps.c')
-rw-r--r--utils/ps.c472
1 files changed, 153 insertions, 319 deletions
diff --git a/utils/ps.c b/utils/ps.c
index de913f74..992b467d 100644
--- a/utils/ps.c
+++ b/utils/ps.c
@@ -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;
}