summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils/ps.c416
1 files changed, 333 insertions, 83 deletions
diff --git a/utils/ps.c b/utils/ps.c
index e8ddcddf..03640ff1 100644
--- a/utils/ps.c
+++ b/utils/ps.c
@@ -30,7 +30,7 @@
#include "error.h"
#include "common.h"
-
+
/* ---------------------------------------------------------------- */
static void
@@ -49,27 +49,37 @@ usage(status)
-a, --all-users List other users' processes\n\
--fmt=FMT Use the output-format FMT\n\
FMT may be `default', `user', `vmem', `long',\n\
- `jobc', `hurd', `hurd-long', or a format-string\n\
+ `jobc', `full', `hurd', `hurd-long',\n\
+ or a custom format-string\n\
+ -d List all processes except process group leaders\n\
+ -e, --all List all processes\n\
+ -f Use the `full' output-format\n\
-g Include session leaders\n\
-H, --no-header Don't print a descriptive header line\n\
-j Use the `jobc' output-format\n\
-l Use the `long' output-format\n\
- --login[=LID] Add the processes from the login collection LID\n\
+ --login[=LID] Add the processes from the login collection LID\n\
(which defaults that of the current process)\n\
-M, --no-msg-port Don't show info that uses a process's msg port\n\
- -o, --owner=USER Show processes owned by USER\n\
- -P, --no-parent Include processes without parents\n\
- -Q, --all-fields Don't elide unusable fields (normally if there's\n\
+ -oUSER, --owner=USER Show processes owned by USER\n\
+ -pPID, --pid=PID List the process PID\n\
+ --pgrp=PGRP List processes in the process group PGRP\n\
+ -P, --no-parent Include processes without parents\n\
+ -Q, --all-fields Don't elide unusable fields (normally if there's\n\
some reason ps can't print a field for any\n\
process, it's removed from the output entirely)\n\
-r, --reverse Reverse the order of any sort\n\
- --session[=SID] Add the processes from the session SID (which\n\
- defaults to the sid of the current process)\n
+ --session[=SID] Add the processes from the session SID (which\n\
+ defaults to the sid of the current process)\n\
--sort=FIELD Sort the output with respect to FIELD\n\
- -t, --threads Show the threads for each process\n\
+ -s, --threads Show the threads for each process\n\
+ -tTTY, --tty=TTY Only show processes who's controlling tty is TTY\n\
-u Use the `user' output-format\n\
-v Use the `vmem' output-format\n\
-x Include orphaned processes\n\
+\n\
+The USER, LID, PID, PGRP, and SID arguments may also be comma separated lists.\n\
+The System V options -u and -g may be accessed with -o and --pgrp.\n\
");
}
@@ -77,7 +87,7 @@ usage(status)
}
int
-enum_name(char *arg, char **choices, char *kind, int allow_mismatches)
+parse_enum(char *arg, char **choices, char *kind, bool allow_mismatches)
{
int arglen = strlen(arg);
char **p = choices;
@@ -103,7 +113,7 @@ enum_name(char *arg, char **choices, char *kind, int allow_mismatches)
return partial_match;
else
{
- fprintf(stderr, "%s: invalid %s", arg, kind);
+ fprintf(stderr, "%s: Invalid %s", arg, kind);
usage(1);
return 0;
}
@@ -111,7 +121,7 @@ enum_name(char *arg, char **choices, char *kind, int allow_mismatches)
/* ---------------------------------------------------------------- */
-#define FILTER_OWN 0x01
+#define FILTER_OWNER 0x01
#define FILTER_NSESSLDR 0x02
#define FILTER_CTTY 0x04
#define FILTER_UNORPHANED 0x08
@@ -124,28 +134,35 @@ enum procsets
char *procset_names[] =
{"all", "session", "login", 0};
-/* -1 is EOF */
+/* Long options without corresponding short ones. -1 is EOF. */
#define OPT_LOGIN -2
#define OPT_SESSION -3
#define OPT_SORT -4
#define OPT_FMT -5
#define OPT_HELP -6
+#define OPT_PGRP -7
-#define SHORT_OPTIONS "agHjlMPQrtuvx"
+#define SHORT_OPTIONS "adefgHjlMo:p:PQrt::suvx"
static struct option options[] =
{
+ {"all", no_argument, 0, 'e'},
{"all-users", no_argument, 0, 'a'},
{"all-fields", no_argument, 0, 'Q'},
{"fmt", required_argument, 0, OPT_FMT},
{"help", no_argument, 0, OPT_HELP},
{"login", optional_argument, 0, OPT_LOGIN},
+ {"lid", optional_argument, 0, OPT_LOGIN},
{"no-header", no_argument, 0, 'H'},
{"no-msg-port", no_argument, 0, 'M'},
{"no-squash", no_argument, 0, 'Q'},
{"no-parent", no_argument, 0, 'P'},
+ {"owner", required_argument, 0, 'o'},
+ {"pgrp", required_argument, 0, OPT_PGRP},
{"session", optional_argument, 0, OPT_SESSION},
- {"threads", no_argument, 0, 't'},
+ {"sid", optional_argument, 0, OPT_SESSION},
+ {"threads", no_argument, 0, 's'},
+ {"tty", optional_argument, 0, 't'},
{"reverse", no_argument, 0, 'r'},
{"sort", required_argument, 0, OPT_SORT},
{0, 0, 0, 0}
@@ -153,13 +170,13 @@ static struct option options[] =
/* The names of various predefined output formats. */
char *fmt_names[] =
- {"default", "user", "vmem", "long", "jobc", "hurd", "hurd-long", 0};
+ {"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", "%cpu", "%mem", "pid", "pid", "pid", "pid", "pid"};
/* and whether the sort should be backwards or forwards; */
bool fmt_sortrev[] =
- {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE};
+ {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE};
/* and the actual format strings. */
char *fmts[] =
{
@@ -173,40 +190,176 @@ char *fmts[] =
"~UID ~PID ~TH# ~PPID ~PRI ~NI=bpri ~TH=nth ~MSGIN ~MSGOUT ~SZ=vsize ~RSS=rsize ~STAT=state ~TT=tty ~TIME ~COMMAND=args",
/* jobc (-j) */
"~USER ~PID ~TH# ~PPID ~PGRP ~SESS ~LCOLL ~STAT=state ~TT=tty ~TIME ~COMMAND=args",
+ /* full (-f) (from sysv) */
+ "~-USER ~PID ~PPID ~TTY ~TIME ~COMMAND=args",
/* hurd */
"~PID ~Th# ~UID ~NTh ~VMem=vsize ~RSS=rsize ~User=utime ~System=stime ~Args",
/* hurd-long */
"~PID ~Th# ~UID ~PPID ~PGRP ~Sess ~NTh ~VMem=vsize ~RSS=rsize ~%CPU ~User=utime ~System=stime ~Args"
};
+
+/* ---------------------------------------------------------------- */
+
+/* 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)(char *str), void (*default_add_fn)(),
+ 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, "No %s specified", 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)(char *str),
+ char *(*default_fn)(),
+ 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)(char *str),
+ char *type_name)
+{
+ void default_num_add() { (*add_fn)((*default_fn)()); }
+ void add_num_str(char *str)
+ {
+ char *p;
+ for (p = str; *p != '\0'; p++)
+ if (!isdigit(*p))
+ {
+ (*add_fn)((*lookup_fn)(str));
+ return;
+ }
+ (*add_fn)(atoi(str));
+ }
+ _parse_strlist(arg, add_num_str, default_num_add, 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 uid_t
+lookup_user(char *name)
+{
+ struct passwd *pw = getpwnam(name);
+ if (pw == NULL)
+ error(2, 0, "%s: Unknown user", name);
+ return pw->pw_uid;
+}
+
+/* ---------------------------------------------------------------- */
void
main(int argc, char *argv[])
{
int opt;
error_t err;
- ps_fmt_t fmt;
+ unsigned num_uids = 0, uids_alloced = 1;
+ uid_t *uids = malloc(sizeof(uid_t) * num_uids);
+ unsigned num_tty_names = 0, tty_names_alloced = 1;
+ char **tty_names = malloc(sizeof(char *) * num_tty_names);
proc_stat_list_t procset;
- process_t cur_proc = getproc();
ps_context_t context;
- int cur_pid = getpid();
+ ps_fmt_t fmt;
char *fmt_string = "default", *sort_key_name = NULL;
- int filter_mask =
- FILTER_OWN | FILTER_NSESSLDR | FILTER_UNORPHANED | FILTER_PARENTED;
+ unsigned filter_mask =
+ FILTER_OWNER | FILTER_NSESSLDR | FILTER_UNORPHANED | FILTER_PARENTED;
bool sort_reverse = FALSE, print_heading = TRUE, squash_bogus_fields = TRUE;
bool show_threads = FALSE, no_msg_port = FALSE;
- /* Handle an argument that is a process ID. */
- void pid_arg (const char *arg)
+ /* Add a specific process to be printed out. */
+ void add_pid (unsigned pid)
{
- int pid = atoi (arg);
-
- if (pid == 0 && strcmp (arg, "0") != 0)
- error(3, 0, "%s: invalid process id", argv[optind]);
-
err = proc_stat_list_add_pid(procset, pid);
if (err)
- error(3, err, "%d: can't add process id", pid);
-
+ error(3, err, "%d: Can't add process id", 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
@@ -214,8 +367,101 @@ main(int argc, char *argv[])
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);
+ if (err)
+ error(2, err, "Couldn't add session");
+ }
+ /* 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);
+ if (err)
+ error(2, err, "Couldn't add login collection");
+ }
+ /* Print out all process from the given process group. */
+ void add_pgrp(unsigned pgrp)
+ {
+ error_t err = proc_stat_list_add_pgrp(procset, pgrp);
+ if (err)
+ error(2, err, "Couldn't add process group");
+ }
+
+ /* Add a user who's processes should be printed out. */
+ void add_uid (unsigned uid)
+ {
+ if (uids_alloced == num_uids)
+ {
+ uids_alloced *= 2;
+ uids = realloc(uids, uids_alloced * sizeof(int));
+ if (uids == NULL)
+ error(8, ENOMEM, "Can't allocate uid list");
+ }
+ uids[num_uids++] = uid;
+ }
+ /* Returns TRUE if PS is owned by any of the users in UIDS. */
+ bool proc_stat_has_owner(struct proc_stat *ps)
+ {
+ unsigned i;
+ uid_t uid = proc_stat_info(ps)->owner;
+ for (i = 0; i < num_uids; i++)
+ if (uids[i] == uid)
+ return TRUE;
+ return FALSE;
+ }
+
+ /* Add TTY_NAME to the list for which processes with those controlling
+ terminals will be printed. */
+ void add_tty_name (char *tty_name)
+ {
+ if (tty_names_alloced == num_tty_names)
+ {
+ tty_names_alloced *= 2;
+ tty_names = realloc(tty_names, tty_names_alloced * sizeof(int));
+ if (tty_names == NULL)
+ error(8, ENOMEM, "Can't allocate tty_name list");
+ }
+ tty_names[num_tty_names++] = tty_name;
+ }
+ bool proc_stat_has_ctty(struct proc_stat *ps)
+ {
+ ps_tty_t tty = proc_stat_tty(ps);
+ if (tty)
+ {
+ unsigned i;
+ char *name = ps_tty_name(tty);
+ char *short_name = ps_tty_short_name(tty);
+
+ for (i = 0; i < num_tty_names; i++)
+ if ((name && strcmp (tty_names[i], name) == 0)
+ || (short_name && strcmp (tty_names[i], short_name) == 0))
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /* Returns the name of the current controlling terminal. */
+ static char *current_tty_name()
+ {
+ error_t err;
+ ps_tty_t tty;
+ mach_port_t cttyid = getcttyid();
- err = ps_context_create(cur_proc, &context);
+ if (cttyid == MACH_PORT_NULL)
+ error(2, 0, "No controlling terminal");
+
+ err = ps_context_find_tty_by_cttyid(context, cttyid, &tty);
+ if (err)
+ error(2, err, "Can't get controlling terminal");
+
+ return ps_tty_name(tty);
+ }
+
+ proc_server = getproc();
+
+ err = ps_context_create(proc_server, &context);
if (err)
error(1, err, "ps_context_create");
@@ -223,32 +469,40 @@ main(int argc, char *argv[])
if (err)
error(1, err, "proc_stat_list_create");
+ /* Parse our options. */
while ((opt = getopt_long(argc, argv, "-" SHORT_OPTIONS, options, 0)) != EOF)
switch (opt)
{
- case 1:
- /* Non-option argument. */
- if (isdigit (optarg[0]))
- pid_arg (optarg);
- else
+ case 1: /* Non-option argument. */
+ if (!isdigit(*optarg))
+ /* Old-fashioned `ps' syntax takes options without the leading dash.
+ Prepend a dash and feed back to getopt. */
{
- /* Old-fashioned `ps' syntax takes options without the leading
- dash. Prepend a dash and feed back to getopt. */
size_t len = strlen (optarg) + 1;
argv[--optind] = alloca (1 + len);
argv[optind][0] = '-';
memcpy (&argv[optind][1], optarg, len);
+ break;
}
+ /* Otherwise, fall through and treat the arg as a process id. */
+ case 'p':
+ parse_numlist(optarg, add_pid, NULL, NULL, "PID");
break;
case 'a':
- filter_mask &= ~(FILTER_OWN | FILTER_NSESSLDR); break;
+ filter_mask &= ~(FILTER_OWNER | FILTER_NSESSLDR); break;
+ case 'd':
+ filter_mask &= ~(FILTER_OWNER | FILTER_UNORPHANED); break;
+ case 'e':
+ filter_mask = 0; break;
case 'g':
filter_mask &= ~FILTER_NSESSLDR; break;
case 'x':
filter_mask &= ~FILTER_UNORPHANED; break;
case 'P':
filter_mask &= ~FILTER_PARENTED; break;
+ case 'f':
+ fmt_string = "full"; break;
case 'u':
fmt_string = "user"; break;
case 'v':
@@ -263,7 +517,7 @@ main(int argc, char *argv[])
print_heading = FALSE; break;
case 'Q':
squash_bogus_fields = FALSE; break;
- case 't':
+ case 's':
show_threads = TRUE; break;
case OPT_FMT:
fmt_string = optarg; break;
@@ -271,45 +525,37 @@ main(int argc, char *argv[])
sort_key_name = optarg; break;
case 'r':
sort_reverse = TRUE; break;
- case OPT_SESSION:
- {
- int sid = (optarg == NULL ? -1 : atoi(optarg));
- if (sid < 0) {
- /* Get the current session; a bit of a pain */
- err = proc_getsid(cur_proc, cur_pid, &sid);
- if (err)
- error(2, err, "Couldn't get current session id");
- }
-
- err = proc_stat_list_add_session(procset, sid);
- if (err)
- error(2, err, "Couldn't add session pids");
- }
+ case 't':
+ parse_strlist(optarg, add_tty_name, current_tty_name, "tty");
+ break;
+ case 'o':
+ parse_numlist(optarg, add_uid, NULL, lookup_user, "user");
+ break;
+ case OPT_SESSION:
+ parse_numlist(optarg, add_sid, current_sid, NULL, "session id");
break;
case OPT_LOGIN:
- {
- int login_coll = (optarg == NULL ? -1 : atoi(optarg));
-
- if (login_coll < 0) {
- err = proc_getloginid(cur_proc, cur_pid, &login_coll);
- if (err)
- error(2, err, "Couldn't get current login collection");
- }
-
- err = proc_stat_list_add_login_coll(procset, login_coll);
- if (err)
- error(2, err, "Couldn't add login collection pids");
- }
+ parse_numlist(optarg, add_lid, current_lid, NULL, "login collection");
+ break;
+ case OPT_PGRP:
+ parse_numlist(optarg, add_pgrp, NULL, NULL, "process group");
break;
+
case OPT_HELP:
usage(0);
default:
usage(1);
}
+ while (argv[optind] != NULL)
+ parse_numlist(argv[optind++], add_pid, NULL, NULL, "PID");
+
+ if (num_uids == 0 && (filter_mask & FILTER_OWNER))
+ add_uid(getuid());
+
{
- int fmt_index = enum_name(fmt_string, fmt_names, "format type", 1);
+ int fmt_index = parse_enum(fmt_string, fmt_names, "format type", 1);
if (fmt_index >= 0)
{
fmt_string = fmts[fmt_index];
@@ -321,9 +567,6 @@ main(int argc, char *argv[])
}
}
- while (argv[optind] != NULL)
- pid_arg (argv[optind++]);
-
if (proc_stat_list_num_procs(procset) == 0)
{
err = proc_stat_list_add_all(procset);
@@ -334,8 +577,12 @@ main(int argc, char *argv[])
if (no_msg_port)
proc_stat_list_set_flags(procset, PSTAT_NO_MSGPORT);
- if (filter_mask & FILTER_OWN)
- proc_stat_list_filter(procset, &ps_own_filter, FALSE);
+ /* Filter out any processes that we don't want to show. */
+ if (num_uids > 0)
+ proc_stat_list_filter1(procset, proc_stat_has_owner, PSTAT_INFO, FALSE);
+ if (num_tty_names > 0)
+ /* Returns TRUE if PS has for a controlling terminal any in TTY_NAMES. */
+ proc_stat_list_filter1(procset, proc_stat_has_ctty, PSTAT_TTY, FALSE);
if (filter_mask & FILTER_NSESSLDR)
proc_stat_list_filter(procset, &ps_not_sess_leader_filter, FALSE);
if (filter_mask & FILTER_UNORPHANED)
@@ -356,7 +603,7 @@ main(int argc, char *argv[])
the printed title and not the official name. */
{
ps_fmt_spec_t sort_key = NULL;
- int nfields = ps_fmt_num_fields(fmt);
+ unsigned nfields = ps_fmt_num_fields(fmt);
ps_fmt_field_t field = ps_fmt_fields(fmt);
/* first, look at the actual printed titles in the current format */
@@ -388,7 +635,7 @@ main(int argc, char *argv[])
/* Remove any fields that we can't print anyway (because of system
bugs/protection violations?). */
{
- int bogus_flags = ps_fmt_needs(fmt);
+ ps_flags_t bogus_flags = ps_fmt_needs(fmt);
err = proc_stat_list_find_bogus_flags(procset, &bogus_flags);
if (err)
@@ -398,12 +645,15 @@ main(int argc, char *argv[])
}
if (print_heading)
- {
- err = ps_fmt_write_titles(fmt, stdout, NULL);
- if (err)
- error(0, err, "Can't print titles");
- putc('\n', stdout);
- }
+ if (proc_stat_list_num_procs(procset) > 0)
+ {
+ err = ps_fmt_write_titles(fmt, stdout, NULL);
+ if (err)
+ error(0, err, "Can't print titles");
+ putc('\n', stdout);
+ }
+ else
+ error(0, 0, "No applicable processes");
err = proc_stat_list_fmt(procset, fmt, stdout, NULL);
if (err)