diff options
Diffstat (limited to 'utils/ps.c')
-rw-r--r-- | utils/ps.c | 416 |
1 files changed, 333 insertions, 83 deletions
@@ -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) |