Support more POSIX/SUSv3 options:

- Change `-p' to allow a list of process IDs, and `-t' to allow a list
  of terminal names, instead of only a single value for each.
- Add the `-A' option of SUSv3, which is exactly the same as `-ax'.
- Add the `-G gidlist' (group id).
- Allow any of these "selector options" to be specified multiple times,
  and have `ps' keep adding to a given list -- instead of replacing the
  previously-specified values.
- Fix interactions between selector-options, so that: "If any are
  specified, ... ps shall select the processes represented by the
  inclusive OR of all the selection-criteria options." (from SUSv3)
- Add a `-X' option, which is the reverse of the `-x' option.

- various minor improvements in parsing and error handling.

This does not get us to match POSIX/SUSv3, but it gets us closer.  The
`-g pgidlist', `-R ruserlist' and `-s sidlist' options mentioned in
freebsd-standards are still under debate, so they skipped for now.
It should be true that this introduces no user-visible incompatible
changes, except to support "new stuff" that was not supported before.
This commit is contained in:
Garance A Drosehn 2004-03-27 18:22:17 +00:00
parent b2ae7ed72c
commit a4c8a745a8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=127499
2 changed files with 587 additions and 194 deletions

View File

@ -40,7 +40,11 @@
.Nd process status
.Sh SYNOPSIS
.Nm
.Op Fl aCcefHhjlmrSTuvwxZ
.Op Fl aCcefHhjlmrSTuvwXxZ
.Oo Fl G Ar gid Ns Xo
.Op , Ns Ar gid Ns No ...
.Xc
.Oc
.Op Fl M Ar core
.Op Fl N Ar system
.Op Fl O Ar fmt
@ -49,7 +53,10 @@
.Op , Ns Ar pid Ns No ...
.Xc
.Oc
.Op Fl t Ar tty
.Oo Fl t Ar tty Ns Xo
.Op , Ns Ar tty Ns No ...
.Xc
.Oc
.Oo Fl U Ar username Ns Xo
.Op , Ns Ar username Ns No ...
.Xc
@ -65,7 +72,19 @@ processes that have controlling terminals.
This information is sorted by controlling terminal, then by process
.Tn ID .
.Pp
The information displayed is selected based on a set of keywords (see the
A different set of processes can be selected for display by using any
combination of the
.Fl a, G , p , T , t
and
.Fl U
options.
If more than one of these options are given, then
.Nm
will select all processes which are matched by at least one of the
given options.
.Pp
For the processes which have been selected for display, the information
to display is selected based on a set of keywords (see the
.Fl L
.Fl O
and
@ -86,6 +105,10 @@ The options are as follows:
.Bl -tag -width indent
.It Fl a
Display information about other users' processes as well as your own.
This will skip any processes which do not have a controlling teminal,
unless the
.Fl x
option is also specified.
This can be disabled by setting the
.Va security.bsd.see_other_uids
sysctl to zero.
@ -101,6 +124,10 @@ Display the environment as well.
.It Fl f
Show commandline and environment information about swapped out processes.
This option is honored only if the uid of the user is 0.
.It Fl G
Display information about processes which are running with the specified
real group
.Tn ID(s) .
.It Fl H
Show all of the
.Em kernel visible
@ -147,7 +174,7 @@ Keywords may be appended with an equals (``='') sign and a string.
This causes the printed header to use the specified string instead of
the standard header.
.It Fl p
Display information associated with the specified process
Display information about processes which match the specified process
.Tn ID(s) .
.It Fl r
Sort by current cpu usage, instead of by process
@ -160,7 +187,7 @@ Display information about processes attached to the device associated
with the standard input.
.It Fl t
Display information about processes attached to the specified terminal
device.
device(s).
.It Fl U
Display the processes belonging to the specified
.Ar username Ns (s) .
@ -189,8 +216,22 @@ If the
option is specified more than once,
.Nm
will use as many columns as necessary without regard for your window size.
.It Fl X
When displaying processes matched by other options, skip any processes
which do not have a controlling terminal.
.It Fl x
Display information about processes without controlling terminals.
When displaying processes matched by other options, include processes
which do not have a controlling terminal.
This is the opposite of the
.Fl X
option.
If both
.Fl X
and
.Fl x
are specified in the same command, then
.Nm
will use the one which was specified last.
.It Fl Z
Add label to the list of keywords for which
.Nm
@ -542,6 +583,14 @@ the mount point of
.Xr pstat 8 ,
.Xr sysctl 8 ,
.Xr mutex 9
.Sh STANDARDS
For historical reasons,
.Nm
utility under
.Fx
supports a different set of options from what is described by
.St -p1003.2 ,
and what is supported on non-BSD operating systems.
.Sh HISTORY
The
.Nm

View File

@ -29,6 +29,13 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ------+---------+---------+-------- + --------+---------+---------+---------*
* Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
* All rights reserved.
*
* Significant modifications made to bring `ps' options somewhat closer
* to the standard for `ps' as described in SingleUnixSpec-v3.
* ------+---------+---------+-------- + --------+---------+---------+---------*
*/
#ifndef lint
@ -56,11 +63,13 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <kvm.h>
#include <limits.h>
#include <locale.h>
#include <paths.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -68,13 +77,15 @@ __FBSDID("$FreeBSD$");
#include "ps.h"
#define SEP ", \t" /* username separators */
#define W_SEP " \t" /* "Whitespace" list separators */
#define T_SEP "," /* "Terminate-element" list separators */
static KINFO *kinfo;
struct varent *vhead;
int eval; /* exit value */
int cflag; /* -c */
int optfatal; /* Fatal error parsing some list-option */
int rawcpu; /* -C */
int sumrusage; /* -S */
int termwidth; /* width of screen (0 == infinity) */
@ -82,6 +93,24 @@ int totwidth; /* calculated width of requested variables */
time_t now; /* current time(3) value */
struct listinfo;
typedef int addelem_rtn(struct listinfo *_inf, const char *elem);
struct listinfo {
int count;
int maxcount;
int elemsize;
addelem_rtn *addelem;
const char *lname;
union {
gid_t *gids;
pid_t *pids;
dev_t *ttys;
uid_t *uids;
void *ptr;
};
};
static int needuser, needcomm, needenv;
#if defined(LAZY_PS)
static int forceuread=0;
@ -100,8 +129,15 @@ static void scanvars(void);
static void dynsizevars(KINFO *);
static void sizevars(void);
static void usage(void);
static pid_t *getpids(const char *, int *);
static uid_t *getuids(const char *, int *);
static int addelem_gid(struct listinfo *, const char *);
static int addelem_pid(struct listinfo *, const char *);
static int addelem_tty(struct listinfo *, const char *);
static int addelem_uid(struct listinfo *, const char *);
static void add_list(struct listinfo *, const char *);
static void *expand_list(struct listinfo *);
static void free_list(struct listinfo *);
static void init_list(struct listinfo *, addelem_rtn, int, const char *);
static char dfmt[] = "pid,tt,state,time,command";
static char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
@ -115,23 +151,22 @@ static char Zfmt[] = "label";
static kvm_t *kd;
#if defined(LAZY_PS)
#define PS_ARGS "aCcefgHhjLlM:mN:O:o:p:rSTt:U:uvwxZ"
#define PS_ARGS "AaCcefG:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
#else
#define PS_ARGS "aCcegHhjLlM:mN:O:o:p:rSTt:U:uvwxZ"
#define PS_ARGS "AaCceG:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
#endif
int
main(int argc, char *argv[])
{
struct listinfo gidlist, pgrplist, pidlist;
struct listinfo ruidlist, sesslist, ttylist, uidlist;
struct kinfo_proc *kp;
struct varent *vent;
struct winsize ws;
dev_t ttydev;
pid_t *pids;
uid_t *uids;
int all, ch, dropgid, flag, _fmt, i, lineno;
int nentries, nocludge, noutput, npids, nuids, pid;
int prtheader, showthreads, uid, wflag, what, xflg;
int all, ch, dropgid, elem, flag, _fmt, i, lineno;
int nentries, nocludge, nkept, nselectors;
int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
char *cols;
char errbuf[_POSIX2_LINE_MAX];
const char *cp, *nlistf, *memf;
@ -170,16 +205,32 @@ main(int argc, char *argv[])
argv[1] = kludge_oldps_options(argv[1]);
}
all = _fmt = prtheader = wflag = xflg = 0;
npids = nuids = 0;
pids = uids = NULL;
ttydev = NODEV;
xkeep = -1; /* Neither -x nor -X */
all = _fmt = nselectors = prtheader = wflag = xkeep_implied = 0;
init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
dropgid = 0;
optfatal = 0;
memf = nlistf = _PATH_DEVNULL;
showthreads = 0;
while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
switch((char)ch) {
case 'A':
/*
* Exactly the same as `-ax'. This has been
* added for compatability with SUSv3, but for
* now it will not be described in the man page.
*/
nselectors++;
all = xkeep = 1;
break;
case 'a':
nselectors++;
all = 1;
break;
case 'C':
@ -191,8 +242,24 @@ main(int argc, char *argv[])
case 'e': /* XXX set ufmt */
needenv = 1;
break;
case 'G':
add_list(&gidlist, optarg);
xkeep_implied = 1;
nselectors++;
break;
#if 0
/* XXX - This SUSv3 option is still under debate. */
/* (it conflicts with the undocumented `-g' option) */
case 'g':
add_list(&pgrplist, optarg);
xkeep_implied = 1;
nselectors++;
break;
#else
case 'g':
/* Historical BSD-ish (from SunOS) option */
break; /* no-op */
#endif
case 'H':
showthreads = KERN_PROC_INC_THREAD;
break;
@ -241,40 +308,54 @@ main(int argc, char *argv[])
break;
#endif
case 'p':
pids = getpids(optarg, &npids);
xflg = 1;
add_list(&pidlist, optarg);
/*
* Note: `-p' does not *set* xkeep, but any values
* from pidlist are checked before xkeep is. That
* way they are always matched, even if the user
* specifies `-X'.
*/
nselectors++;
break;
#if 0
/* XXX - This un-standard option is still under debate. */
case 'R':
/* This is what SUSv3 defines as the `-U' option. */
add_list(&ruidlist, optarg);
xkeep_implied = 1;
nselectors++;
break;
#endif
case 'r':
sortby = SORTCPU;
break;
case 'S':
sumrusage = 1;
break;
#if 0
/* XXX - This non-standard option is still under debate. */
/* (it conflicts with `-s' in NetBSD) */
case 's':
/* As seen on Solaris, Linux, IRIX. */
add_list(&sesslist, optarg);
xkeep_implied = 1;
nselectors++;
break;
#endif
case 'T':
if ((optarg = ttyname(STDIN_FILENO)) == NULL)
errx(1, "stdin: not a terminal");
/* FALLTHROUGH */
case 't': {
struct stat sb;
char *ttypath, pathbuf[PATH_MAX];
if (strcmp(optarg, "co") == 0)
ttypath = strdup(_PATH_CONSOLE);
else if (*optarg != '/')
(void)snprintf(ttypath = pathbuf,
sizeof(pathbuf), "%s%s", _PATH_TTY, optarg);
else
ttypath = optarg;
if (stat(ttypath, &sb) == -1)
err(1, "%s", ttypath);
if (!S_ISCHR(sb.st_mode))
errx(1, "%s: not a terminal", ttypath);
ttydev = sb.st_rdev;
case 't':
add_list(&ttylist, optarg);
xkeep_implied = 1;
nselectors++;
break;
}
case 'U':
uids = getuids(optarg, &nuids);
xflg++; /* XXX: intuitive? */
/* This is what SUSv3 defines as the `-u' option. */
add_list(&uidlist, optarg);
xkeep_implied = 1;
nselectors++;
break;
case 'u':
parsefmt(ufmt, 0);
@ -295,8 +376,22 @@ main(int argc, char *argv[])
termwidth = 131;
wflag++;
break;
case 'X':
/*
* Note that `-X' and `-x' are not standard "selector"
* options. For most selector-options, we check *all*
* processes to see if any are matched by the given
* value(s). After we have a set of all the matched
* processes, then `-X' and `-x' govern whether we
* modify that *matched* set for processes which do
* not have a controlling terminal. `-X' causes
* those processes to be deleted from the matched
* set, while `-x' causes them to be kept.
*/
xkeep = 0;
break;
case 'x':
xflg = 1;
xkeep = 1;
break;
case 'Z':
parsefmt(Zfmt, 0);
@ -309,6 +404,12 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (optfatal)
exit(1); /* Error messages already printed */
if (xkeep < 0) /* Neither -X nor -x was specified */
xkeep = xkeep_implied;
#define BACKWARD_COMPATIBILITY
#ifdef BACKWARD_COMPATIBILITY
if (*argv) {
@ -334,12 +435,13 @@ main(int argc, char *argv[])
if (!_fmt)
parsefmt(dfmt, 0);
/* XXX - should be cleaner */
if (!all && ttydev == NODEV && !npids && !nuids) {
if ((uids = malloc(sizeof (*uids))) == NULL)
if (nselectors == 0) {
uidlist.ptr = malloc(sizeof(uid_t));
if (uidlist.ptr == NULL)
errx(1, "malloc failed");
nuids = 1;
*uids = getuid();
nselectors = 1;
uidlist.count = uidlist.maxcount = 1;
*uidlist.uids = getuid();
}
/*
@ -347,37 +449,126 @@ main(int argc, char *argv[])
* and adjusting header widths as appropriate.
*/
scanvars();
/*
* get proc list
* Get process list. If the user requested just one selector-
* option, then kvm_getprocs can be asked to return just those
* processes. Otherwise, have it return all processes, and
* then this routine will search that full list and select the
* processes which match any of the user's selector-options.
*/
if (nuids == 1) {
what = KERN_PROC_UID | showthreads;
flag = *uids;
} else if (ttydev != NODEV) {
what = KERN_PROC_TTY | showthreads;
flag = ttydev;
} else if (npids == 1) {
what = KERN_PROC_PID | showthreads;
flag = *pids;
} else {
what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
flag = 0;
what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
flag = 0;
if (nselectors == 1) {
/* XXX - Apparently there's no KERN_PROC_GID flag. */
if (pgrplist.count == 1) {
what = KERN_PROC_PGRP | showthreads;
flag = *pgrplist.pids;
nselectors = 0;
} else if (pidlist.count == 1) {
what = KERN_PROC_PID | showthreads;
flag = *pidlist.pids;
nselectors = 0;
} else if (ruidlist.count == 1) {
what = KERN_PROC_RUID | showthreads;
flag = *ruidlist.uids;
nselectors = 0;
#if 0 /* XXX - KERN_PROC_SESSION causes error in kvm_getprocs? */
} else if (sesslist.count == 1) {
what = KERN_PROC_SESSION | showthreads;
flag = *sesslist.pids;
nselectors = 0;
#endif
} else if (ttylist.count == 1) {
what = KERN_PROC_TTY | showthreads;
flag = *ttylist.ttys;
nselectors = 0;
} else if (uidlist.count == 1) {
what = KERN_PROC_UID | showthreads;
flag = *uidlist.uids;
nselectors = 0;
} else if (all) {
/* No need for this routine to select processes. */
nselectors = 0;
}
}
/*
* select procs
*/
nentries = -1;
kp = kvm_getprocs(kd, what, flag, &nentries);
if ((kp == 0 && nentries != 0) || nentries < 0)
errx(1, "%s", kvm_geterr(kd));
nkept = 0;
if (nentries > 0) {
if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
errx(1, "malloc failed");
for (i = nentries; --i >= 0; ++kp) {
kinfo[i].ki_p = kp;
/*
* If the user specified multiple selection-criteria,
* then keep any process matched by the inclusive OR
* of all the selection-criteria given.
*/
if (pidlist.count > 0) {
for (elem = 0; elem < pidlist.count; elem++)
if (kp->ki_pid == pidlist.pids[elem])
goto keepit;
}
/*
* Note that we had to process pidlist before
* filtering out processes which do not have
* a controlling terminal.
*/
if (xkeep == 0) {
if ((kp->ki_tdev == NODEV ||
(kp->ki_flag & P_CONTROLT) == 0))
continue;
}
if (nselectors == 0)
goto keepit;
if (gidlist.count > 0) {
for (elem = 0; elem < gidlist.count; elem++)
if (kp->ki_rgid == gidlist.gids[elem])
goto keepit;
}
if (pgrplist.count > 0) {
for (elem = 0; elem < pgrplist.count; elem++)
if (kp->ki_pgid == pgrplist.pids[elem])
goto keepit;
}
if (ruidlist.count > 0) {
for (elem = 0; elem < ruidlist.count; elem++)
if (kp->ki_ruid == ruidlist.uids[elem])
goto keepit;
}
if (sesslist.count > 0) {
for (elem = 0; elem < sesslist.count; elem++)
if (kp->ki_sid == sesslist.pids[elem])
goto keepit;
}
if (ttylist.count > 0) {
for (elem = 0; elem < ttylist.count; elem++)
if (kp->ki_tdev == ttylist.ttys[elem])
goto keepit;
}
if (uidlist.count > 0) {
for (elem = 0; elem < uidlist.count; elem++)
if (kp->ki_uid == uidlist.uids[elem])
goto keepit;
}
/*
* This process did not match any of the user's
* selector-options, so skip the process.
*/
continue;
keepit:
kinfo[nkept].ki_p = kp;
if (needuser)
saveuser(&kinfo[i]);
dynsizevars(&kinfo[i]);
saveuser(&kinfo[nkept]);
dynsizevars(&kinfo[nkept]);
nkept++;
}
}
@ -387,169 +578,321 @@ main(int argc, char *argv[])
* print header
*/
printheader();
if (nentries == 0)
if (nkept == 0)
exit(1);
/*
* sort proc list
*/
qsort(kinfo, nentries, sizeof(KINFO), pscomp);
qsort(kinfo, nkept, sizeof(KINFO), pscomp);
/*
* for each proc, call each variable output function.
* For each process, call each variable output function.
*/
noutput = 0;
for (i = lineno = 0; i < nentries; i++) {
if (xflg == 0 && ((&kinfo[i])->ki_p->ki_tdev == NODEV ||
((&kinfo[i])->ki_p->ki_flag & P_CONTROLT ) == 0))
continue;
if (npids > 1) {
for (pid = 0; pid < npids; pid++)
if ((&kinfo[i])->ki_p->ki_pid == pids[pid])
break;
if (pid == npids)
continue;
}
if (nuids > 1) {
for (uid = 0; uid < nuids; uid++)
if ((&kinfo[i])->ki_p->ki_uid == uids[uid])
break;
if (uid == nuids)
continue;
}
for (i = lineno = 0; i < nkept; i++) {
for (vent = vhead; vent; vent = vent->next) {
(vent->var->oproc)(&kinfo[i], vent);
if (vent->next != NULL)
(void)putchar(' ');
}
(void)putchar('\n');
noutput++;
if (prtheader && lineno++ == prtheader - 4) {
(void)putchar('\n');
printheader();
lineno = 0;
}
}
if (pids != NULL)
free(pids);
if (uids != NULL)
free(uids);
/* Check if '-p proclist' or '-u userlist' matched zero processes */
if (noutput == 0)
eval = 1;
free_list(&gidlist);
free_list(&pidlist);
free_list(&pgrplist);
free_list(&ruidlist);
free_list(&sesslist);
free_list(&ttylist);
free_list(&uidlist);
exit(eval);
}
#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h */
pid_t *
getpids(const char *arg, int *npids)
static int
addelem_gid(struct listinfo *inf, const char *elem)
{
char copyarg[32];
char *copyp, *endp;
pid_t *pids, *morepids;
int alloc;
long tempid;
struct group *grp;
intmax_t ltemp;
const char *nameorID;
char *endp;
alloc = 0;
*npids = 0;
pids = NULL;
while (*arg != '\0') {
while (*arg != '\0' && strchr(SEP, *arg) != NULL)
arg++;
if (*arg == '\0' || strchr(SEP, *arg) != NULL)
tempid = 0;
else {
copyp = copyarg;
endp = copyarg + sizeof(copyarg) - 1;
while (*arg != '\0' && strchr(SEP, *arg) == NULL &&
copyp <= endp)
*copyp++ = *arg++;
if (copyp > endp) {
*endp = '\0';
tempid = -1;
while (*arg != '\0' &&
strchr(SEP, *arg) == NULL)
arg++;
} else {
*copyp = '\0';
errno = 0;
tempid = strtol(copyarg, &endp, 10);
}
/*
* Write warning messages for any values which
* would never be a valid process number.
*/
if (*endp != '\0' || tempid < 0 || copyarg == endp) {
warnx("invalid process number: %s", copyarg);
errno = ERANGE;
} else if (errno != 0 || tempid > BSD_PID_MAX) {
warnx("process number too large: %s", copyarg);
errno = ERANGE;
}
if (errno == ERANGE) {
/* Ignore this value from the given list. */
continue;
}
}
if (*npids >= alloc) {
alloc = (alloc + 1) << 1;
morepids = realloc(pids, alloc * sizeof (*pids));
if (morepids == NULL) {
free(pids);
errx(1, "realloc failed");
}
pids = morepids;
}
pids[(*npids)++] = tempid;
if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
if (*elem == '\0')
warnx("Invalid (zero-length) %s name", inf->lname);
else
warnx("%s name too long: %s", inf->lname, elem);
optfatal = 1;
return (0); /* Do not add this value */
}
if (!*npids)
errx(1, "No valid process numbers specified");
/*
* SUSv3 states that `ps -G grouplist' should match "real-group
* ID numbers", and does not mention group-names. I do want to
* also support group-names, so this tries for a group-id first,
* and only tries for a name if that doesn't work. This is the
* opposite order of what is done in addelem_uid(), but in
* practice the order would only matter for group-names which
* are all-numeric.
*/
grp = NULL;
nameorID = "named";
errno = 0;
ltemp = strtol(elem, &endp, 10);
if (errno == 0 && *endp == '\0' && ltemp >= 0 && ltemp <= GID_MAX) {
nameorID = "name or ID matches";
grp = getgrgid((gid_t)ltemp);
}
if (grp == NULL)
grp = getgrnam(elem);
if (grp == NULL) {
warnx("No %s %s '%s'", inf->lname, nameorID, elem);
optfatal = 1;
return (0); /* Do not add this value */
}
return (pids);
if (inf->count >= inf->maxcount)
expand_list(inf);
inf->gids[(inf->count)++] = grp->gr_gid;
return (1);
}
uid_t *
getuids(const char *arg, int *nuids)
#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h */
static int
addelem_pid(struct listinfo *inf, const char *elem)
{
char name[MAXLOGNAME];
struct passwd *pwd;
uid_t *uids, *moreuids;
int alloc;
size_t l;
long tempid;
char *endp;
alloc = 0;
*nuids = 0;
uids = NULL;
for (; (l = strcspn(arg, SEP)) > 0; arg += l + strspn(arg + l, SEP)) {
if (l >= sizeof name) {
warnx("%.*s: name too long", (int)l, arg);
continue;
if (*elem == '\0')
tempid = 0L;
else {
errno = 0;
tempid = strtol(elem, &endp, 10);
if (*endp != '\0' || tempid < 0 || elem == endp) {
warnx("Invalid %s: %s", inf->lname, elem);
errno = ERANGE;
} else if (errno != 0 || tempid > BSD_PID_MAX) {
warnx("%s too large: %s", inf->lname, elem);
errno = ERANGE;
}
strncpy(name, arg, l);
name[l] = '\0';
if ((pwd = getpwnam(name)) == NULL) {
warnx("%s: no such user", name);
continue;
if (errno == ERANGE) {
optfatal = 1;
return (0); /* Do not add this value */
}
if (*nuids >= alloc) {
alloc = (alloc + 1) << 1;
moreuids = realloc(uids, alloc * sizeof (*uids));
if (moreuids == NULL) {
free(uids);
errx(1, "realloc failed");
}
uids = moreuids;
}
uids[(*nuids)++] = pwd->pw_uid;
}
endpwent();
if (!*nuids)
errx(1, "No users specified");
if (inf->count >= inf->maxcount)
expand_list(inf);
inf->pids[(inf->count)++] = tempid;
return (1);
}
#undef BSD_PID_MAX
return uids;
static int
addelem_tty(struct listinfo *inf, const char *elem)
{
char pathbuf[PATH_MAX];
struct stat sb;
const char *ttypath;
if (strcmp(elem, "co") == 0)
ttypath = strdup(_PATH_CONSOLE);
else if (*elem == '/')
ttypath = elem;
else {
strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf));
strlcat(pathbuf, elem, sizeof(pathbuf));
ttypath = pathbuf;
}
if (stat(ttypath, &sb) == -1) {
warn("%s", ttypath);
optfatal = 1;
return (0); /* Do not add this value */
}
if (!S_ISCHR(sb.st_mode)) {
warn("%s: Not a terminal", ttypath);
optfatal = 1;
return (0); /* Do not add this value */
}
if (inf->count >= inf->maxcount)
expand_list(inf);
inf->ttys[(inf->count)++] = sb.st_rdev;
return (1);
}
static int
addelem_uid(struct listinfo *inf, const char *elem)
{
struct passwd *pwd;
intmax_t ltemp;
char *endp;
if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
if (*elem == '\0')
warnx("Invalid (zero-length) %s name", inf->lname);
else
warnx("%s name too long: %s", inf->lname, elem);
optfatal = 1;
return (0); /* Do not add this value */
}
/*
* XXX - In the following, should the warnings be fatal errors?
* Historically they have been warnings, but I expect errors
* would be more appropriate. A warning message might be
* missed in a long `ps' listing, and the user would not
* realize that they had mistyped a userid. Also, Solaris
* and Linux treat these as errors. On the other hand, I
* can imagine that some users *might* be used to the
* present behavior.
*/
pwd = getpwnam(elem);
if (pwd == NULL) {
errno = 0;
ltemp = strtol(elem, &endp, 10);
if (errno != 0 || *endp != '\0' || ltemp < 0 ||
ltemp > UID_MAX)
warnx("No %s named '%s'", inf->lname, elem);
else {
/* The string is all digits, so it might be a userID. */
pwd = getpwuid((uid_t)ltemp);
if (pwd == NULL)
warnx("No %s name or ID matches '%s'",
inf->lname, elem);
}
}
if (pwd == NULL) {
/* XXX: optfatal = 1; -- (see the above XXX) */
return (0); /* Do not add this value */
}
if (inf->count >= inf->maxcount)
expand_list(inf);
inf->uids[(inf->count)++] = pwd->pw_uid;
return (1);
}
static void
add_list(struct listinfo *inf, const char *argp)
{
char elemcopy[PATH_MAX];
const char *savep;
char *cp, *endp;
int toolong;
while (*argp != '\0') {
while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
argp++;
savep = argp;
toolong = 0;
cp = elemcopy;
if (strchr(T_SEP, *argp) == NULL) {
endp = elemcopy + sizeof(elemcopy) - 1;
while (*argp != '\0' && cp <= endp &&
strchr(W_SEP T_SEP, *argp) == NULL)
*cp++ = *argp++;
if (cp > endp)
toolong = 1;
}
if (!toolong) {
*cp = '\0';
#ifndef ADD_PS_LISTRESET
/* This is how the standard expects lists to be handled. */
inf->addelem(inf, elemcopy);
#else
/*
* This would add a simple non-standard-but-convienent feature.
*
* XXX - Adding this check increased the total size of `ps' by
* 3940 bytes on i386! That's 12% of the entire program!
* The `ps.o' file grew by only about 40 bytes, but the
* final (stripped) executable in /bin/ps grew by 12%.
*/
/*
* We now have a single element. Add it to the
* list, unless the element is ":". In that case,
* reset the list so previous entries are ignored.
*/
if (strcmp(elemcopy, ":") == 0)
inf->count = 0;
else
inf->addelem(inf, elemcopy);
#endif
} else {
/*
* The string is too long to copy. Find the end
* of the string to print out the warning message.
*/
while (*argp != '\0' && strchr(W_SEP T_SEP,
*argp) == NULL)
argp++;
warnx("Value too long: %.*s", (int)(argp - savep),
savep);
optfatal = 1;
}
/*
* Skip over any number of trailing whitespace characters,
* but only one (at most) trailing element-terminating
* character.
*/
while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
argp++;
if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
argp++;
/* Catch case where string ended with a comma. */
if (*argp == '\0')
inf->addelem(inf, argp);
}
}
}
static void *
expand_list(struct listinfo *inf)
{
int newmax;
void *newlist;
newmax = (inf->maxcount + 1) << 1;
newlist = realloc(inf->ptr, newmax * inf->elemsize);
if (newlist == NULL) {
free(inf->ptr);
errx(1, "realloc to %d %ss failed", newmax,
inf->lname);
}
inf->maxcount = newmax;
inf->ptr = newlist;
return (newlist);
}
static void
free_list(struct listinfo *inf)
{
inf->count = inf->elemsize = inf->maxcount = 0;
if (inf->ptr != NULL)
free(inf->ptr);
inf->addelem = NULL;
inf->lname = NULL;
inf->ptr = NULL;
}
static void
init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
const char *lname)
{
inf->count = inf->maxcount = 0;
inf->elemsize = elemsize;
inf->addelem = artn;
inf->lname = lname;
inf->ptr = NULL;
}
VARENT *
@ -758,9 +1101,10 @@ static void
usage(void)
{
(void)fprintf(stderr, "%s\n%s\n%s\n",
"usage: ps [-aCHhjlmrSTuvwxZ] [-O|o fmt] [-p pid[,pid]] [-t tty]",
" [-U user[,user]] [-M core] [-N system]",
(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
"usage: ps [-aCHhjlmrSTuvwXxZ] [-G gid[,gid]] [-O|o fmt]",
" [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
" [-M core] [-N system]",
" ps [-L]");
exit(1);
}