Index: stable/7/bin/ps/ps.c =================================================================== --- stable/7/bin/ps/ps.c (revision 204288) +++ stable/7/bin/ps/ps.c (revision 204289) @@ -1,1343 +1,1344 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 . * All rights reserved. * * Significant modifications made to bring `ps' options somewhat closer * to the standard for `ps' as described in SingleUnixSpec-v3. * ------+---------+---------+-------- + --------+---------+---------+---------* */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps.h" #define _PATH_PTS "/dev/pts/" #define W_SEP " \t" /* "Whitespace" list separators */ #define T_SEP "," /* "Terminate-element" list separators */ #ifdef LAZY_PS #define DEF_UREAD 0 #define OPT_LAZY_f "f" #else #define DEF_UREAD 1 /* Always do the more-expensive read. */ #define OPT_LAZY_f /* I.e., the `-f' option is not added. */ #endif /* * isdigit takes an `int', but expects values in the range of unsigned char. * This wrapper ensures that values from a 'char' end up in the correct range. */ #define isdigitch(Anychar) isdigit((u_char)(Anychar)) int cflag; /* -c */ int eval; /* Exit value */ time_t now; /* Current time(3) value */ int rawcpu; /* -C */ int sumrusage; /* -S */ int termwidth; /* Width of the screen (0 == infinity). */ int totwidth; /* Calculated-width of requested variables. */ int showthreads; /* will threads be shown? */ struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist); static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ static kvm_t *kd; static KINFO *kinfo; static int needcomm; /* -o "command" */ static int needenv; /* -e */ static int needuser; /* -o "user" */ static int optfatal; /* Fatal error parsing some list-option. */ static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 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; } l; }; static int check_procfs(void); 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 descendant_sort(KINFO *, int); static void dynsizevars(KINFO *); static void *expand_list(struct listinfo *); static const char * fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), KINFO *, char *, int); static void free_list(struct listinfo *); static void init_list(struct listinfo *, addelem_rtn, int, const char *); static char *kludge_oldps_options(const char *, char *, const char *); static int pscomp(const void *, const void *); static void saveuser(KINFO *); static void scanvars(void); static void sizevars(void); static void usage(void); static char dfmt[] = "pid,tt,state,time,command"; static char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command"; static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," "tt,time,command"; static char o1[] = "pid"; static char o2[] = "tt,state,time,command"; static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," "%cpu,%mem,command"; static char Zfmt[] = "label"; #define PS_ARGS "AaCcde" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ" int main(int argc, char *argv[]) { struct listinfo gidlist, pgrplist, pidlist; struct listinfo ruidlist, sesslist, ttylist, uidlist; struct kinfo_proc *kp; KINFO *next_KINFO; struct varent *vent; struct winsize ws; const char *nlistf, *memf; char *cols; int all, ch, elem, flag, _fmt, i, lineno; int descendancy, nentries, nkept, nselectors; int prtheader, wflag, what, xkeep, xkeep_implied; char errbuf[_POSIX2_LINE_MAX]; (void) setlocale(LC_ALL, ""); time(&now); /* Used by routines in print.c. */ if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') termwidth = atoi(cols); else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || ws.ws_col == 0) termwidth = 79; else termwidth = ws.ws_col - 1; /* * Hide a number of option-processing kludges in a separate routine, * to support some historical BSD behaviors, such as `ps axu'. */ if (argc > 1) argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]); all = descendancy = _fmt = nselectors = optfatal = 0; prtheader = showthreads = wflag = xkeep_implied = 0; xkeep = -1; /* Neither -x nor -X. */ 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"); - memf = nlistf = _PATH_DEVNULL; + memf = _PATH_DEVNULL; + nlistf = NULL; 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': rawcpu = 1; break; case 'c': cflag = 1; break; case 'd': descendancy = 1; break; case 'e': /* XXX set ufmt */ needenv = 1; break; #ifdef LAZY_PS case 'f': if (getuid() == 0 || getgid() == 0) forceuread = 1; break; #endif case 'G': add_list(&gidlist, optarg); xkeep_implied = 1; nselectors++; break; case 'g': #if 0 /*- * XXX - This SUSv3 behavior is still under debate * since it conflicts with the (undocumented) * `-g' option. So we skip it for now. */ add_list(&pgrplist, optarg); xkeep_implied = 1; nselectors++; break; #else /* The historical BSD-ish (from SunOS) behavior. */ break; /* no-op */ #endif case 'H': showthreads = KERN_PROC_INC_THREAD; break; case 'h': prtheader = ws.ws_row > 5 ? ws.ws_row : 22; break; case 'j': parsefmt(jfmt, 0); _fmt = 1; jfmt[0] = '\0'; break; case 'L': showkey(); exit(0); case 'l': parsefmt(lfmt, 0); _fmt = 1; lfmt[0] = '\0'; break; case 'M': memf = optarg; break; case 'm': sortby = SORTMEM; break; case 'N': nlistf = optarg; break; case 'O': parsefmt(o1, 1); parsefmt(optarg, 1); parsefmt(o2, 1); o1[0] = o2[0] = '\0'; _fmt = 1; break; case 'o': parsefmt(optarg, 1); _fmt = 1; break; case 'p': 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 case 'R': /*- * XXX - This un-standard option is still under * debate. This is what SUSv3 defines as * the `-U' option, and while it would be * nice to have, it could cause even more * confusion to implement it as `-R'. */ add_list(&ruidlist, optarg); xkeep_implied = 1; nselectors++; break; #endif case 'r': sortby = SORTCPU; break; case 'S': sumrusage = 1; break; #if 0 case 's': /*- * XXX - This non-standard option is still under * debate. This *is* supported on Solaris, * Linux, and IRIX, but conflicts with `-s' * on NetBSD and maybe some older BSD's. */ 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': add_list(&ttylist, optarg); xkeep_implied = 1; nselectors++; break; case 'U': /* This is what SUSv3 defines as the `-u' option. */ add_list(&uidlist, optarg); xkeep_implied = 1; nselectors++; break; case 'u': parsefmt(ufmt, 0); sortby = SORTCPU; _fmt = 1; ufmt[0] = '\0'; break; case 'v': parsefmt(vfmt, 0); sortby = SORTMEM; _fmt = 1; vfmt[0] = '\0'; break; case 'w': if (wflag) termwidth = UNLIMITED; else if (termwidth < 131) 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': xkeep = 1; break; case 'Z': parsefmt(Zfmt, 0); Zfmt[0] = '\0'; break; case '?': default: usage(); } argc -= optind; argv += optind; /* * If the user specified ps -e then they want a copy of the process * environment kvm_getenvv(3) attempts to open /proc//mem. * Check to make sure that procfs is mounted on /proc, otherwise * print a warning informing the user that output will be incomplete. */ if (needenv == 1 && check_procfs() == 0) warnx("Process environment requires procfs(5)"); /* * If there arguments after processing all the options, attempt * to treat them as a list of process ids. */ while (*argv) { if (!isdigitch(**argv)) break; add_list(&pidlist, *argv); argv++; } if (*argv) { fprintf(stderr, "%s: illegal argument: %s\n", getprogname(), *argv); usage(); } if (optfatal) exit(1); /* Error messages already printed. */ if (xkeep < 0) /* Neither -X nor -x was specified. */ xkeep = xkeep_implied; kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); if (kd == 0) errx(1, "%s", errbuf); if (!_fmt) parsefmt(dfmt, 0); if (nselectors == 0) { uidlist.l.ptr = malloc(sizeof(uid_t)); if (uidlist.l.ptr == NULL) errx(1, "malloc failed"); nselectors = 1; uidlist.count = uidlist.maxcount = 1; *uidlist.l.uids = getuid(); } /* * scan requested variables, noting what structures are needed, * and adjusting header widths as appropriate. */ scanvars(); /* * 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. */ what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; flag = 0; if (nselectors == 1) { if (gidlist.count == 1) { what = KERN_PROC_RGID | showthreads; flag = *gidlist.l.gids; nselectors = 0; } else if (pgrplist.count == 1) { what = KERN_PROC_PGRP | showthreads; flag = *pgrplist.l.pids; nselectors = 0; } else if (pidlist.count == 1) { what = KERN_PROC_PID | showthreads; flag = *pidlist.l.pids; nselectors = 0; } else if (ruidlist.count == 1) { what = KERN_PROC_RUID | showthreads; flag = *ruidlist.l.uids; nselectors = 0; } else if (sesslist.count == 1) { what = KERN_PROC_SESSION | showthreads; flag = *sesslist.l.pids; nselectors = 0; } else if (ttylist.count == 1) { what = KERN_PROC_TTY | showthreads; flag = *ttylist.l.ttys; nselectors = 0; } else if (uidlist.count == 1) { what = KERN_PROC_UID | showthreads; flag = *uidlist.l.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 == NULL && nentries > 0) || (kp != NULL && 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) { /* * 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.l.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.l.gids[elem]) goto keepit; } if (pgrplist.count > 0) { for (elem = 0; elem < pgrplist.count; elem++) if (kp->ki_pgid == pgrplist.l.pids[elem]) goto keepit; } if (ruidlist.count > 0) { for (elem = 0; elem < ruidlist.count; elem++) if (kp->ki_ruid == ruidlist.l.uids[elem]) goto keepit; } if (sesslist.count > 0) { for (elem = 0; elem < sesslist.count; elem++) if (kp->ki_sid == sesslist.l.pids[elem]) goto keepit; } if (ttylist.count > 0) { for (elem = 0; elem < ttylist.count; elem++) if (kp->ki_tdev == ttylist.l.ttys[elem]) goto keepit; } if (uidlist.count > 0) { for (elem = 0; elem < uidlist.count; elem++) if (kp->ki_uid == uidlist.l.uids[elem]) goto keepit; } /* * This process did not match any of the user's * selector-options, so skip the process. */ continue; keepit: next_KINFO = &kinfo[nkept]; next_KINFO->ki_p = kp; next_KINFO->ki_d.level = 0; next_KINFO->ki_d.prefix = NULL; next_KINFO->ki_pcpu = getpcpu(next_KINFO); if (sortby == SORTMEM) next_KINFO->ki_memsize = kp->ki_tsize + kp->ki_dsize + kp->ki_ssize; if (needuser) saveuser(next_KINFO); dynsizevars(next_KINFO); nkept++; } } sizevars(); /* * print header */ printheader(); if (nkept == 0) exit(1); /* * sort proc list */ qsort(kinfo, nkept, sizeof(KINFO), pscomp); /* * We want things in descendant order */ if (descendancy) descendant_sort(kinfo, nkept); /* * For each process, call each variable output function. */ for (i = lineno = 0; i < nkept; i++) { STAILQ_FOREACH(vent, &varlist, next_ve) { (vent->var->oproc)(&kinfo[i], vent); if (STAILQ_NEXT(vent, next_ve) != NULL) (void)putchar(' '); } (void)putchar('\n'); if (prtheader && lineno++ == prtheader - 4) { (void)putchar('\n'); printheader(); lineno = 0; } } free_list(&gidlist); free_list(&pidlist); free_list(&pgrplist); free_list(&ruidlist); free_list(&sesslist); free_list(&ttylist); free_list(&uidlist); for (i = 0; i < nkept; i++) free(kinfo[i].ki_d.prefix); free(kinfo); exit(eval); } static int addelem_gid(struct listinfo *inf, const char *elem) { struct group *grp; const char *nameorID; char *endp; u_long bigtemp; 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. */ } /* * 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; bigtemp = strtoul(elem, &endp, 10); if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { nameorID = "name or ID matches"; grp = getgrgid((gid_t)bigtemp); } if (grp == NULL) grp = getgrnam(elem); if (grp == NULL) { warnx("No %s %s '%s'", inf->lname, nameorID, elem); optfatal = 1; return (0); } if (inf->count >= inf->maxcount) expand_list(inf); inf->l.gids[(inf->count)++] = grp->gr_gid; return (1); } #define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */ static int addelem_pid(struct listinfo *inf, const char *elem) { char *endp; long tempid; if (*elem == '\0') { warnx("Invalid (zero-length) process id"); optfatal = 1; return (0); /* Do not add this value. */ } 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; } if (errno == ERANGE) { optfatal = 1; return (0); } if (inf->count >= inf->maxcount) expand_list(inf); inf->l.pids[(inf->count)++] = tempid; return (1); } #undef BSD_PID_MAX /*- * The user can specify a device via one of three formats: * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0 * 2) missing "/dev", e.g.: ttyp0 console pts/0 * 3) two-letters, e.g.: p0 co 0 * (matching letters that would be seen in the "TT" column) */ static int addelem_tty(struct listinfo *inf, const char *elem) { const char *ttypath; struct stat sb; char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX]; ttypath = NULL; pathbuf2[0] = '\0'; pathbuf3[0] = '\0'; switch (*elem) { case '/': ttypath = elem; break; case 'c': if (strcmp(elem, "co") == 0) { ttypath = _PATH_CONSOLE; break; } /* FALLTHROUGH */ default: strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf)); strlcat(pathbuf, elem, sizeof(pathbuf)); ttypath = pathbuf; if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0) break; if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0) break; if (strcmp(pathbuf, _PATH_CONSOLE) == 0) break; /* Check to see if /dev/tty${elem} exists */ strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2)); strlcat(pathbuf2, elem, sizeof(pathbuf2)); if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) { /* No need to repeat stat() && S_ISCHR() checks */ ttypath = NULL; break; } /* Check to see if /dev/pts/${elem} exists */ strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3)); strlcat(pathbuf3, elem, sizeof(pathbuf3)); if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) { /* No need to repeat stat() && S_ISCHR() checks */ ttypath = NULL; break; } break; } if (ttypath) { if (stat(ttypath, &sb) == -1) { if (pathbuf3[0] != '\0') warn("%s, %s, and %s", pathbuf3, pathbuf2, ttypath); else warn("%s", ttypath); optfatal = 1; return (0); } if (!S_ISCHR(sb.st_mode)) { if (pathbuf3[0] != '\0') warnx("%s, %s, and %s: Not a terminal", pathbuf3, pathbuf2, ttypath); else warnx("%s: Not a terminal", ttypath); optfatal = 1; return (0); } } if (inf->count >= inf->maxcount) expand_list(inf); inf->l.ttys[(inf->count)++] = sb.st_rdev; return (1); } static int addelem_uid(struct listinfo *inf, const char *elem) { struct passwd *pwd; char *endp; u_long bigtemp; 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. */ } pwd = getpwnam(elem); if (pwd == NULL) { errno = 0; bigtemp = strtoul(elem, &endp, 10); if (errno != 0 || *endp != '\0' || bigtemp > 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)bigtemp); if (pwd == NULL) warnx("No %s name or ID matches '%s'", inf->lname, elem); } } if (pwd == NULL) { /* * These used to be treated as minor warnings (and the * option was simply ignored), but now they are fatal * errors (and the command will be aborted). */ optfatal = 1; return (0); } if (inf->count >= inf->maxcount) expand_list(inf); inf->l.uids[(inf->count)++] = pwd->pw_uid; return (1); } static void add_list(struct listinfo *inf, const char *argp) { const char *savep; char *cp, *endp; int toolong; char elemcopy[PATH_MAX]; if (*argp == 0) inf->addelem(inf, elemcopy); 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'; /* * Add this single element to the given list. */ inf->addelem(inf, elemcopy); } 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 descendant_sort(KINFO *ki, int items) { int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src; unsigned char *path; KINFO kn; /* * First, sort the entries by descendancy, tracking the descendancy * depth in the ki_d.level field. */ src = 0; maxlvl = 0; while (src < items) { if (ki[src].ki_d.level) { src++; continue; } for (nsrc = 1; src + nsrc < items; nsrc++) if (!ki[src + nsrc].ki_d.level) break; for (dst = 0; dst < items; dst++) { if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid) continue; if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid) break; } if (dst == items) { src += nsrc; continue; } for (ndst = 1; dst + ndst < items; ndst++) if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level) break; for (n = src; n < src + nsrc; n++) { ki[n].ki_d.level += ki[dst].ki_d.level + 1; if (maxlvl < ki[n].ki_d.level) maxlvl = ki[n].ki_d.level; } while (nsrc) { if (src < dst) { kn = ki[src]; memmove(ki + src, ki + src + 1, (dst - src + ndst - 1) * sizeof *ki); ki[dst + ndst - 1] = kn; nsrc--; dst--; ndst++; } else if (src != dst + ndst) { kn = ki[src]; memmove(ki + dst + ndst + 1, ki + dst + ndst, (src - dst - ndst) * sizeof *ki); ki[dst + ndst] = kn; ndst++; nsrc--; src++; } else { ndst += nsrc; src += nsrc; nsrc = 0; } } } /* * Now populate ki_d.prefix (instead of ki_d.level) with the command * prefix used to show descendancies. */ path = malloc((maxlvl + 7) / 8); memset(path, '\0', (maxlvl + 7) / 8); for (src = 0; src < items; src++) { if ((lvl = ki[src].ki_d.level) == 0) { ki[src].ki_d.prefix = NULL; continue; } if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL) errx(1, "malloc failed"); for (n = 0; n < lvl - 2; n++) { ki[src].ki_d.prefix[n * 2] = path[n / 8] & 1 << (n % 8) ? '|' : ' '; ki[src].ki_d.prefix[n * 2 + 1] = ' '; } if (n == lvl - 2) { /* Have I any more siblings? */ for (siblings = 0, dst = src + 1; dst < items; dst++) { if (ki[dst].ki_d.level > lvl) continue; if (ki[dst].ki_d.level == lvl) siblings = 1; break; } if (siblings) path[n / 8] |= 1 << (n % 8); else path[n / 8] &= ~(1 << (n % 8)); ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`'; ki[src].ki_d.prefix[n * 2 + 1] = '-'; n++; } strcpy(ki[src].ki_d.prefix + n * 2, "- "); } free(path); } static void * expand_list(struct listinfo *inf) { void *newlist; int newmax; newmax = (inf->maxcount + 1) << 1; newlist = realloc(inf->l.ptr, newmax * inf->elemsize); if (newlist == NULL) { free(inf->l.ptr); errx(1, "realloc to %d %ss failed", newmax, inf->lname); } inf->maxcount = newmax; inf->l.ptr = newlist; return (newlist); } static void free_list(struct listinfo *inf) { inf->count = inf->elemsize = inf->maxcount = 0; if (inf->l.ptr != NULL) free(inf->l.ptr); inf->addelem = NULL; inf->lname = NULL; inf->l.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->l.ptr = NULL; } VARENT * find_varentry(VAR *v) { struct varent *vent; STAILQ_FOREACH(vent, &varlist, next_ve) { if (strcmp(vent->var->name, v->name) == 0) return vent; } return NULL; } static void scanvars(void) { struct varent *vent; VAR *v; STAILQ_FOREACH(vent, &varlist, next_ve) { v = vent->var; if (v->flag & DSIZ) { v->dwidth = v->width; v->width = 0; } if (v->flag & USER) needuser = 1; if (v->flag & COMM) needcomm = 1; } } static void dynsizevars(KINFO *ki) { struct varent *vent; VAR *v; int i; STAILQ_FOREACH(vent, &varlist, next_ve) { v = vent->var; if (!(v->flag & DSIZ)) continue; i = (v->sproc)( ki); if (v->width < i) v->width = i; if (v->width > v->dwidth) v->width = v->dwidth; } } static void sizevars(void) { struct varent *vent; VAR *v; int i; STAILQ_FOREACH(vent, &varlist, next_ve) { v = vent->var; i = strlen(vent->header); if (v->width < i) v->width = i; totwidth += v->width + 1; /* +1 for space */ } totwidth--; } static const char * fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, char *comm, int maxlen) { const char *s; s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); return (s); } #define UREADOK(ki) (forceuread || (ki->ki_p->ki_flag & P_INMEM)) static void saveuser(KINFO *ki) { if (ki->ki_p->ki_flag & P_INMEM) { /* * The u-area might be swapped out, and we can't get * at it because we have a crashdump and no swap. * If it's here fill in these fields, otherwise, just * leave them 0. */ ki->ki_valid = 1; } else ki->ki_valid = 0; /* * save arguments if needed */ if (needcomm) { if (ki->ki_p->ki_stat == SZOMB) ki->ki_args = strdup(""); else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL)) ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm, MAXCOMLEN)); else asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); if (ki->ki_args == NULL) errx(1, "malloc failed"); } else { ki->ki_args = NULL; } if (needenv) { if (UREADOK(ki)) ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0)); else ki->ki_env = strdup("()"); if (ki->ki_env == NULL) errx(1, "malloc failed"); } else { ki->ki_env = NULL; } } /* A macro used to improve the readability of pscomp(). */ #define DIFF_RETURN(a, b, field) do { \ if ((a)->field != (b)->field) \ return (((a)->field < (b)->field) ? -1 : 1); \ } while (0) static int pscomp(const void *a, const void *b) { const KINFO *ka, *kb; ka = a; kb = b; /* SORTCPU and SORTMEM are sorted in descending order. */ if (sortby == SORTCPU) DIFF_RETURN(kb, ka, ki_pcpu); if (sortby == SORTMEM) DIFF_RETURN(kb, ka, ki_memsize); /* * TTY's are sorted in ascending order, except that all NODEV * processes come before all processes with a device. */ if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) { if (ka->ki_p->ki_tdev == NODEV) return (-1); if (kb->ki_p->ki_tdev == NODEV) return (1); DIFF_RETURN(ka, kb, ki_p->ki_tdev); } /* PID's and TID's (threads) are sorted in ascending order. */ DIFF_RETURN(ka, kb, ki_p->ki_pid); DIFF_RETURN(ka, kb, ki_p->ki_tid); return (0); } #undef DIFF_RETURN /* * ICK (all for getopt), would rather hide the ugliness * here than taint the main code. * * ps foo -> ps -foo * ps 34 -> ps -p34 * * The old convention that 't' with no trailing tty arg means the users * tty, is only supported if argv[1] doesn't begin with a '-'. This same * feature is available with the option 'T', which takes no argument. */ static char * kludge_oldps_options(const char *optlist, char *origval, const char *nextarg) { size_t len; char *argp, *cp, *newopts, *ns, *optp, *pidp; /* * See if the original value includes any option which takes an * argument (and will thus use up the remainder of the string). */ argp = NULL; if (optlist != NULL) { for (cp = origval; *cp != '\0'; cp++) { optp = strchr(optlist, *cp); if ((optp != NULL) && *(optp + 1) == ':') { argp = cp; break; } } } if (argp != NULL && *origval == '-') return (origval); /* * if last letter is a 't' flag with no argument (in the context * of the oldps options -- option string NOT starting with a '-' -- * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). * * However, if a flag accepting a string argument is found earlier * in the option string (including a possible `t' flag), then the * remainder of the string must be the argument to that flag; so * do not modify that argument. Note that a trailing `t' would * cause argp to be set, if argp was not already set by some * earlier option. */ len = strlen(origval); cp = origval + len - 1; pidp = NULL; if (*cp == 't' && *origval != '-' && cp == argp) { if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg)) *cp = 'T'; } else if (argp == NULL) { /* * The original value did not include any option which takes * an argument (and that would include `p' and `t'), so check * the value for trailing number, or comma-separated list of * numbers, which we will treat as a pid request. */ if (isdigitch(*cp)) { while (cp >= origval && (*cp == ',' || isdigitch(*cp))) --cp; pidp = cp + 1; } } /* * If nothing needs to be added to the string, then return * the "original" (although possibly modified) value. */ if (*origval == '-' && pidp == NULL) return (origval); /* * Create a copy of the string to add '-' and/or 'p' to the * original value. */ if ((newopts = ns = malloc(len + 3)) == NULL) errx(1, "malloc failed"); if (*origval != '-') *ns++ = '-'; /* add option flag */ if (pidp == NULL) strcpy(ns, origval); else { /* * Copy everything before the pid string, add the `p', * and then copy the pid string. */ len = pidp - origval; memcpy(ns, origval, len); ns += len; *ns++ = 'p'; strcpy(ns, pidp); } return (newopts); } static int check_procfs(void) { struct statfs mnt; if (statfs("/proc", &mnt) < 0) return (0); if (strcmp(mnt.f_fstypename, "procfs") != 0) return (0); return (1); } static void usage(void) { #define SINGLE_OPTS "[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]" (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]", " [-M core] [-N system]", " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]", " ps [-L]"); exit(1); } Index: stable/7/bin/ps =================================================================== --- stable/7/bin/ps (revision 204288) +++ stable/7/bin/ps (revision 204289) Property changes on: stable/7/bin/ps ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/bin/ps:r203688 Index: stable/7/usr.bin/pkill/pkill.c =================================================================== --- stable/7/usr.bin/pkill/pkill.c (revision 204288) +++ stable/7/usr.bin/pkill/pkill.c (revision 204289) @@ -1,782 +1,783 @@ /* $NetBSD: pkill.c,v 1.16 2005/10/10 22:13:20 kleink Exp $ */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * Copyright (c) 2005 Pawel Jakub Dawidek * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Andrew Doran. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STATUS_MATCH 0 #define STATUS_NOMATCH 1 #define STATUS_BADUSAGE 2 #define STATUS_ERROR 3 #define MIN_PID 5 #define MAX_PID 99999 /* Ignore system-processes (if '-S' flag is not specified) and myself. */ #define PSKIP(kp) ((kp)->ki_pid == mypid || \ (!kthreads && ((kp)->ki_flag & P_KTHREAD) != 0)) enum listtype { LT_GENERIC, LT_USER, LT_GROUP, LT_TTY, LT_PGRP, LT_JID, LT_SID }; struct list { SLIST_ENTRY(list) li_chain; long li_number; }; SLIST_HEAD(listhead, list); static struct kinfo_proc *plist; static char *selected; static const char *delim = "\n"; static int nproc; static int pgrep; static int signum = SIGTERM; static int newest; static int oldest; static int interactive; static int inverse; static int longfmt; static int matchargs; static int fullmatch; static int kthreads; static int cflags = REG_EXTENDED; static kvm_t *kd; static pid_t mypid; static struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); static struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); static struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); static struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); static struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); static struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); static struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); static struct listhead jidlist = SLIST_HEAD_INITIALIZER(list); static void usage(void) __attribute__((__noreturn__)); static int killact(const struct kinfo_proc *); static int grepact(const struct kinfo_proc *); static void makelist(struct listhead *, enum listtype, char *); static int takepid(const char *, int); int main(int argc, char **argv) { char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q, *pidfile; const char *execf, *coref; int ancestors, debug_opt; int i, ch, bestidx, rv, criteria, pidfromfile, pidfilelock; size_t jsz; int (*action)(const struct kinfo_proc *); struct kinfo_proc *kp; struct list *li; struct timeval best_tval; regex_t reg; regmatch_t regmatch; pid_t pid; setlocale(LC_ALL, ""); if (strcmp(getprogname(), "pgrep") == 0) { action = grepact; pgrep = 1; } else { action = killact; p = argv[1]; if (argc > 1 && p[0] == '-') { p++; i = (int)strtol(p, &q, 10); if (*q == '\0') { signum = i; argv++; argc--; } else { if (strncasecmp(p, "sig", 3) == 0) p += 3; for (i = 1; i < NSIG; i++) if (strcasecmp(sys_signame[i], p) == 0) break; if (i != NSIG) { signum = i; argv++; argc--; } } } } ancestors = 0; criteria = 0; debug_opt = 0; pidfile = NULL; pidfilelock = 0; - execf = coref = _PATH_DEVNULL; + execf = NULL; + coref = _PATH_DEVNULL; while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ad:fg:ij:lnos:t:u:vx")) != -1) switch (ch) { case 'D': debug_opt++; break; case 'F': pidfile = optarg; criteria = 1; break; case 'G': makelist(&rgidlist, LT_GROUP, optarg); criteria = 1; break; case 'I': if (pgrep) usage(); interactive = 1; break; case 'L': pidfilelock = 1; break; case 'M': coref = optarg; break; case 'N': execf = optarg; break; case 'P': makelist(&ppidlist, LT_GENERIC, optarg); criteria = 1; break; case 'S': if (!pgrep) usage(); kthreads = 1; break; case 'U': makelist(&ruidlist, LT_USER, optarg); criteria = 1; break; case 'a': ancestors++; break; case 'd': if (!pgrep) usage(); delim = optarg; break; case 'f': matchargs = 1; break; case 'g': makelist(&pgrplist, LT_PGRP, optarg); criteria = 1; break; case 'i': cflags |= REG_ICASE; break; case 'j': makelist(&jidlist, LT_JID, optarg); criteria = 1; break; case 'l': if (!pgrep) usage(); longfmt = 1; break; case 'n': newest = 1; criteria = 1; break; case 'o': oldest = 1; criteria = 1; break; case 's': makelist(&sidlist, LT_SID, optarg); criteria = 1; break; case 't': makelist(&tdevlist, LT_TTY, optarg); criteria = 1; break; case 'u': makelist(&euidlist, LT_USER, optarg); criteria = 1; break; case 'v': inverse = 1; break; case 'x': fullmatch = 1; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if (argc != 0) criteria = 1; if (!criteria) usage(); if (newest && oldest) errx(STATUS_ERROR, "Options -n and -o are mutually exclusive"); if (pidfile != NULL) pidfromfile = takepid(pidfile, pidfilelock); else { if (pidfilelock) { errx(STATUS_ERROR, "Option -L doesn't make sense without -F"); } pidfromfile = -1; } mypid = getpid(); /* * Retrieve the list of running processes from the kernel. */ kd = kvm_openfiles(execf, coref, NULL, O_RDONLY, buf); if (kd == NULL) errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf); /* * Use KERN_PROC_PROC instead of KERN_PROC_ALL, since we * just want processes and not individual kernel threads. */ plist = kvm_getprocs(kd, KERN_PROC_PROC, 0, &nproc); if (plist == NULL) { errx(STATUS_ERROR, "Cannot get process list (%s)", kvm_geterr(kd)); } /* * Allocate memory which will be used to keep track of the * selection. */ if ((selected = malloc(nproc)) == NULL) { err(STATUS_ERROR, "Cannot allocate memory for %d processes", nproc); } memset(selected, 0, nproc); /* * Refine the selection. */ for (; *argv != NULL; argv++) { if ((rv = regcomp(®, *argv, cflags)) != 0) { regerror(rv, ®, buf, sizeof(buf)); errx(STATUS_BADUSAGE, "Cannot compile regular expression `%s' (%s)", *argv, buf); } for (i = 0, kp = plist; i < nproc; i++, kp++) { if (PSKIP(kp)) { if (debug_opt > 0) fprintf(stderr, "* Skipped %5d %3d %s\n", kp->ki_pid, kp->ki_uid, kp->ki_comm); continue; } if (matchargs && (pargv = kvm_getargv(kd, kp, 0)) != NULL) { jsz = 0; while (jsz < sizeof(buf) && *pargv != NULL) { jsz += snprintf(buf + jsz, sizeof(buf) - jsz, pargv[1] != NULL ? "%s " : "%s", pargv[0]); pargv++; } mstr = buf; } else mstr = kp->ki_comm; rv = regexec(®, mstr, 1, ®match, 0); if (rv == 0) { if (fullmatch) { if (regmatch.rm_so == 0 && regmatch.rm_eo == (off_t)strlen(mstr)) selected[i] = 1; } else selected[i] = 1; } else if (rv != REG_NOMATCH) { regerror(rv, ®, buf, sizeof(buf)); errx(STATUS_ERROR, "Regular expression evaluation error (%s)", buf); } if (debug_opt > 1) { const char *rv_res = "NoMatch"; if (selected[i]) rv_res = "Matched"; fprintf(stderr, "* %s %5d %3d %s\n", rv_res, kp->ki_pid, kp->ki_uid, mstr); } } regfree(®); } for (i = 0, kp = plist; i < nproc; i++, kp++) { if (PSKIP(kp)) continue; if (pidfromfile >= 0 && kp->ki_pid != pidfromfile) { selected[i] = 0; continue; } SLIST_FOREACH(li, &ruidlist, li_chain) if (kp->ki_ruid == (uid_t)li->li_number) break; if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &rgidlist, li_chain) if (kp->ki_rgid == (gid_t)li->li_number) break; if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &euidlist, li_chain) if (kp->ki_uid == (uid_t)li->li_number) break; if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &ppidlist, li_chain) if (kp->ki_ppid == (pid_t)li->li_number) break; if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &pgrplist, li_chain) if (kp->ki_pgid == (pid_t)li->li_number) break; if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &tdevlist, li_chain) { if (li->li_number == -1 && (kp->ki_flag & P_CONTROLT) == 0) break; if (kp->ki_tdev == (dev_t)li->li_number) break; } if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &sidlist, li_chain) if (kp->ki_sid == (pid_t)li->li_number) break; if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { selected[i] = 0; continue; } SLIST_FOREACH(li, &jidlist, li_chain) { /* A particular jail ID, including 0 (not in jail) */ if (kp->ki_jid == (int)li->li_number) break; /* Any jail */ if (kp->ki_jid > 0 && li->li_number == -1) break; } if (SLIST_FIRST(&jidlist) != NULL && li == NULL) { selected[i] = 0; continue; } if (argc == 0) selected[i] = 1; } if (!ancestors) { pid = mypid; while (pid) { for (i = 0, kp = plist; i < nproc; i++, kp++) { if (PSKIP(kp)) continue; if (kp->ki_pid == pid) { selected[i] = 0; pid = kp->ki_ppid; break; } } if (i == nproc) { if (pid == mypid) pid = getppid(); else break; /* Maybe we're in a jail ? */ } } } if (newest || oldest) { best_tval.tv_sec = 0; best_tval.tv_usec = 0; bestidx = -1; for (i = 0, kp = plist; i < nproc; i++, kp++) { if (!selected[i]) continue; if (bestidx == -1) { /* The first entry of the list which matched. */ ; } else if (timercmp(&kp->ki_start, &best_tval, >)) { /* This entry is newer than previous "best". */ if (oldest) /* but we want the oldest */ continue; } else { /* This entry is older than previous "best". */ if (newest) /* but we want the newest */ continue; } /* This entry is better than previous "best" entry. */ best_tval.tv_sec = kp->ki_start.tv_sec; best_tval.tv_usec = kp->ki_start.tv_usec; bestidx = i; } memset(selected, 0, nproc); if (bestidx != -1) selected[bestidx] = 1; } /* * Take the appropriate action for each matched process, if any. */ for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { if (PSKIP(kp)) continue; if (selected[i]) { if (inverse) continue; } else if (!inverse) continue; rv |= (*action)(kp); } exit(rv ? STATUS_MATCH : STATUS_NOMATCH); } static void usage(void) { const char *ustr; if (pgrep) ustr = "[-LSfilnovx] [-d delim]"; else ustr = "[-signal] [-ILfinovx]"; fprintf(stderr, "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n" " [-P ppid] [-U uid] [-g pgrp] [-j jid] [-s sid]\n" " [-t tty] [-u euid] pattern ...\n", getprogname(), ustr); exit(STATUS_BADUSAGE); } static void show_process(const struct kinfo_proc *kp) { char **argv; if ((longfmt || !pgrep) && matchargs && (argv = kvm_getargv(kd, kp, 0)) != NULL) { printf("%d ", (int)kp->ki_pid); for (; *argv != NULL; argv++) { printf("%s", *argv); if (argv[1] != NULL) putchar(' '); } } else if (longfmt || !pgrep) printf("%d %s", (int)kp->ki_pid, kp->ki_comm); else printf("%d", (int)kp->ki_pid); } static int killact(const struct kinfo_proc *kp) { int ch, first; if (interactive) { /* * Be careful, ask before killing. */ printf("kill "); show_process(kp); printf("? "); fflush(stdout); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (first != 'y' && first != 'Y') return (1); } if (kill(kp->ki_pid, signum) == -1) { /* * Check for ESRCH, which indicates that the process * disappeared between us matching it and us * signalling it; don't issue a warning about it. */ if (errno != ESRCH) warn("signalling pid %d", (int)kp->ki_pid); /* * Return 0 to indicate that the process should not be * considered a match, since we didn't actually get to * signal it. */ return (0); } return (1); } static int grepact(const struct kinfo_proc *kp) { show_process(kp); printf("%s", delim); return (1); } static void makelist(struct listhead *head, enum listtype type, char *src) { struct list *li; struct passwd *pw; struct group *gr; struct stat st; const char *cp, *prefix; char *sp, *ep, buf[MAXPATHLEN]; int empty; empty = 1; prefix = _PATH_DEV; while ((sp = strsep(&src, ",")) != NULL) { if (*sp == '\0') usage(); if ((li = malloc(sizeof(*li))) == NULL) { err(STATUS_ERROR, "Cannot allocate %zu bytes", sizeof(*li)); } SLIST_INSERT_HEAD(head, li, li_chain); empty = 0; li->li_number = (uid_t)strtol(sp, &ep, 0); if (*ep == '\0') { switch (type) { case LT_PGRP: if (li->li_number == 0) li->li_number = getpgrp(); break; case LT_SID: if (li->li_number == 0) li->li_number = getsid(mypid); break; case LT_JID: if (li->li_number < 0) errx(STATUS_BADUSAGE, "Negative jail ID `%s'", sp); /* For compatibility with old -j */ if (li->li_number == 0) li->li_number = -1; /* any jail */ break; case LT_TTY: usage(); /* NOTREACHED */ default: break; } continue; } switch (type) { case LT_USER: if ((pw = getpwnam(sp)) == NULL) errx(STATUS_BADUSAGE, "Unknown user `%s'", sp); li->li_number = pw->pw_uid; break; case LT_GROUP: if ((gr = getgrnam(sp)) == NULL) errx(STATUS_BADUSAGE, "Unknown group `%s'", sp); li->li_number = gr->gr_gid; break; case LT_TTY: if (strcmp(sp, "-") == 0) { li->li_number = -1; break; } else if (strcmp(sp, "co") == 0) { cp = "console"; } else { cp = sp; if (strncmp(sp, "tty", 3) != 0) prefix = _PATH_TTY; } snprintf(buf, sizeof(buf), "%s%s", prefix, cp); if (stat(buf, &st) == -1) { if (errno == ENOENT) { errx(STATUS_BADUSAGE, "No such tty: `%s'", sp); } err(STATUS_ERROR, "Cannot access `%s'", sp); } if ((st.st_mode & S_IFCHR) == 0) errx(STATUS_BADUSAGE, "Not a tty: `%s'", sp); li->li_number = st.st_rdev; break; case LT_JID: if (strcmp(sp, "none") == 0) li->li_number = 0; else if (strcmp(sp, "any") == 0) li->li_number = -1; else if (*ep != '\0') errx(STATUS_BADUSAGE, "Invalid jail ID `%s'", sp); break; default: usage(); } } if (empty) usage(); } static int takepid(const char *pidfile, int pidfilelock) { char *endp, line[BUFSIZ]; FILE *fh; long rval; fh = fopen(pidfile, "r"); if (fh == NULL) err(STATUS_ERROR, "Cannot open pidfile `%s'", pidfile); if (pidfilelock) { /* * If we can lock pidfile, this means that daemon is not * running, so would be better not to kill some random process. */ if (flock(fileno(fh), LOCK_EX | LOCK_NB) == 0) { (void)fclose(fh); errx(STATUS_ERROR, "File '%s' can be locked", pidfile); } else { if (errno != EWOULDBLOCK) { errx(STATUS_ERROR, "Error while locking file '%s'", pidfile); } } } if (fgets(line, sizeof(line), fh) == NULL) { if (feof(fh)) { (void)fclose(fh); errx(STATUS_ERROR, "Pidfile `%s' is empty", pidfile); } (void)fclose(fh); err(STATUS_ERROR, "Cannot read from pid file `%s'", pidfile); } (void)fclose(fh); rval = strtol(line, &endp, 10); if (*endp != '\0' && !isspace((unsigned char)*endp)) errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile); else if (rval < MIN_PID || rval > MAX_PID) errx(STATUS_ERROR, "Invalid pid in file `%s'", pidfile); return (rval); } Index: stable/7/usr.bin/pkill =================================================================== --- stable/7/usr.bin/pkill (revision 204288) +++ stable/7/usr.bin/pkill (revision 204289) Property changes on: stable/7/usr.bin/pkill ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/bin/pkill:r203688 Index: stable/7/usr.bin/w/w.c =================================================================== --- stable/7/usr.bin/w/w.c (revision 204288) +++ stable/7/usr.bin/w/w.c (revision 204289) @@ -1,514 +1,515 @@ /*- * Copyright (c) 1980, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #include __FBSDID("$FreeBSD$"); #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif #ifndef lint static const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; #endif /* * w - print system status (who and what) * * This program is similar to the systat command on Tenex/Tops 10/20 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" struct timeval boottime; struct utmp utmp; struct winsize ws; kvm_t *kd; time_t now; /* the current time of day */ int ttywidth; /* width of tty */ int argwidth; /* width of tty */ int header = 1; /* true if -h flag: don't print heading */ int nflag; /* true if -n flag: don't convert addrs */ int dflag; /* true if -d flag: output debug info */ int sortidle; /* sort by idle time */ int use_ampm; /* use AM/PM time */ int use_comma; /* use comma as floats separator */ char **sel_users; /* login array of particular users selected */ /* * One of these per active utmp entry. */ struct entry { struct entry *next; struct utmp utmp; dev_t tdev; /* dev_t of terminal */ time_t idle; /* idle time of terminal in seconds */ struct kinfo_proc *kp; /* `most interesting' proc */ char *args; /* arg list of interesting process */ struct kinfo_proc *dkp; /* debug option proc list */ } *ep, *ehead = NULL, **nextp = &ehead; #define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata) /* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */ #define W_DISPHOSTSIZE 16 static void pr_header(time_t *, int); static struct stat *ttystat(char *, int); static void usage(int); static int this_is_uptime(const char *s); char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */ int main(int argc, char *argv[]) { struct kinfo_proc *kp; struct kinfo_proc *dkp; struct stat *stp; FILE *ut; time_t touched; int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid; const char *memf, *nlistf, *p; char *x_suffix; char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; char fn[MAXHOSTNAMELEN]; char *dot; (void)setlocale(LC_ALL, ""); use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); use_comma = (*nl_langinfo(RADIXCHAR) != ','); /* Are we w(1) or uptime(1)? */ if (this_is_uptime(argv[0]) == 0) { wcmd = 0; p = ""; } else { wcmd = 1; p = "dhiflM:N:nsuw"; } dropgid = 0; - memf = nlistf = _PATH_DEVNULL; + memf = _PATH_DEVNULL; + nlistf = NULL; while ((ch = getopt(argc, argv, p)) != -1) switch (ch) { case 'd': dflag = 1; break; case 'h': header = 0; break; case 'i': sortidle = 1; break; case 'M': header = 0; memf = optarg; dropgid = 1; break; case 'N': nlistf = optarg; dropgid = 1; break; case 'n': nflag = 1; break; case 'f': case 'l': case 's': case 'u': case 'w': warnx("[-flsuw] no longer supported"); /* FALLTHROUGH */ case '?': default: usage(wcmd); } argc -= optind; argv += optind; if (!(_res.options & RES_INIT)) res_init(); _res.retrans = 2; /* resolver timeout to 2 seconds per try */ _res.retry = 1; /* only try once.. */ /* * Discard setgid privileges if not the running kernel so that bad * guys can't print interesting stuff from kernel memory. */ if (dropgid) setgid(getgid()); if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) errx(1, "%s", errbuf); (void)time(&now); if ((ut = fopen(_PATH_UTMP, "r")) == NULL) err(1, "%s", _PATH_UTMP); if (*argv) sel_users = argv; for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { if (utmp.ut_name[0] == '\0') continue; if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE))) continue; /* corrupted record */ ++nusers; if (wcmd == 0) continue; if (sel_users) { int usermatch; char **user; usermatch = 0; for (user = sel_users; !usermatch && *user; user++) if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE)) usermatch = 1; if (!usermatch) continue; } if ((ep = calloc(1, sizeof(struct entry))) == NULL) errx(1, "calloc"); *nextp = ep; nextp = &ep->next; memmove(&ep->utmp, &utmp, sizeof(struct utmp)); ep->tdev = stp->st_rdev; /* * If this is the console device, attempt to ascertain * the true console device dev_t. */ if (ep->tdev == 0) { size_t size; size = sizeof(dev_t); (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0); } touched = stp->st_atime; if (touched < ep->utmp.ut_time) { /* tty untouched since before login */ touched = ep->utmp.ut_time; } if ((ep->idle = now - touched) < 0) ep->idle = 0; } (void)fclose(ut); if (header || wcmd == 0) { pr_header(&now, nusers); if (wcmd == 0) { (void)kvm_close(kd); exit(0); } #define HEADER_USER "USER" #define HEADER_TTY "TTY" #define HEADER_FROM "FROM" #define HEADER_LOGIN_IDLE "LOGIN@ IDLE " #define HEADER_WHAT "WHAT\n" #define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \ sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ (void)printf("%-*.*s %-*.*s %-*.*s %s", UT_NAMESIZE, UT_NAMESIZE, HEADER_USER, UT_LINESIZE, UT_LINESIZE, HEADER_TTY, W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, HEADER_LOGIN_IDLE HEADER_WHAT); } if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) err(1, "%s", kvm_geterr(kd)); for (i = 0; i < nentries; i++, kp++) { if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB) continue; for (ep = ehead; ep != NULL; ep = ep->next) { if (ep->tdev == kp->ki_tdev) { /* * proc is associated with this terminal */ if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) { /* * Proc is 'most interesting' */ if (proc_compare(ep->kp, kp)) ep->kp = kp; } /* * Proc debug option info; add to debug * list using kinfo_proc ki_spare[0] * as next pointer; ptr to ptr avoids the * ptr = long assumption. */ dkp = ep->dkp; ep->dkp = kp; debugproc(kp) = dkp; } } } if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) ttywidth = 79; else ttywidth = ws.ws_col - 1; argwidth = ttywidth - WUSED; if (argwidth < 4) argwidth = 8; for (ep = ehead; ep != NULL; ep = ep->next) { if (ep->kp == NULL) { ep->args = strdup("-"); continue; } ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), ep->kp->ki_comm, MAXCOMLEN); if (ep->args == NULL) err(1, NULL); } /* sort by idle time */ if (sortidle && ehead != NULL) { struct entry *from, *save; from = ehead; ehead = NULL; while (from != NULL) { for (nextp = &ehead; (*nextp) && from->idle >= (*nextp)->idle; nextp = &(*nextp)->next) continue; save = from; from = from->next; save->next = *nextp; *nextp = save; } } for (ep = ehead; ep != NULL; ep = ep->next) { char host_buf[UT_HOSTSIZE + 1]; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss; time_t t; int isaddr; host_buf[UT_HOSTSIZE] = '\0'; strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE); p = *host_buf ? host_buf : "-"; if ((x_suffix = strrchr(p, ':')) != NULL) { if ((dot = strchr(x_suffix, '.')) != NULL && strchr(dot+1, '.') == NULL) *x_suffix++ = '\0'; else x_suffix = NULL; } if (!nflag) { /* Attempt to change an IP address into a name */ isaddr = 0; memset(&ss, '\0', sizeof(ss)); if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { lsin6->sin6_len = sizeof(*lsin6); lsin6->sin6_family = AF_INET6; isaddr = 1; } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { lsin->sin_len = sizeof(*lsin); lsin->sin_family = AF_INET; isaddr = 1; } if (isaddr && realhostname_sa(fn, sizeof(fn), sa, sa->sa_len) == HOSTNAME_FOUND) p = fn; } if (x_suffix) { (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); p = buf; } if (dflag) { for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { const char *ptr; ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), dkp->ki_comm, MAXCOMLEN); if (ptr == NULL) ptr = "-"; (void)printf("\t\t%-9d %s\n", dkp->ki_pid, ptr); } } (void)printf("%-*.*s %-*.*s %-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, UT_LINESIZE, UT_LINESIZE, strncmp(ep->utmp.ut_line, "tty", 3) && strncmp(ep->utmp.ut_line, "cua", 3) ? ep->utmp.ut_line : ep->utmp.ut_line + 3, W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); t = _time_to_time32(ep->utmp.ut_time); longattime = pr_attime(&t, &now); longidle = pr_idle(ep->idle); (void)printf("%.*s\n", argwidth - longidle - longattime, ep->args); } (void)kvm_close(kd); exit(0); } static void pr_header(time_t *nowp, int nusers) { double avenrun[3]; time_t uptime; struct timespec tp; int days, hrs, i, mins, secs; char buf[256]; /* * Print time of day. */ if (strftime(buf, sizeof(buf), use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) (void)printf("%s ", buf); /* * Print how long system has been up. */ if (clock_gettime(CLOCK_MONOTONIC, &tp) != -1) { uptime = tp.tv_sec; if (uptime > 60) uptime += 30; days = uptime / 86400; uptime %= 86400; hrs = uptime / 3600; uptime %= 3600; mins = uptime / 60; secs = uptime % 60; (void)printf(" up"); if (days > 0) (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); if (hrs > 0 && mins > 0) (void)printf(" %2d:%02d,", hrs, mins); else if (hrs > 0) (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); else if (mins > 0) (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); else (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); } /* Print number of users logged in to system */ (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); /* * Print 1, 5, and 15 minute load averages. */ if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) (void)printf(", no load average information available\n"); else { (void)printf(", load averages:"); for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) { if (use_comma && i > 0) (void)printf(","); (void)printf(" %.2f", avenrun[i]); } (void)printf("\n"); } } static struct stat * ttystat(char *line, int sz) { static struct stat sb; char ttybuf[MAXPATHLEN]; (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); if (stat(ttybuf, &sb) == 0) { return (&sb); } else return (NULL); } static void usage(int wcmd) { if (wcmd) (void)fprintf(stderr, "usage: w [-dhin] [-M core] [-N system] [user ...]\n"); else (void)fprintf(stderr, "usage: uptime\n"); exit(1); } static int this_is_uptime(const char *s) { const char *u; if ((u = strrchr(s, '/')) != NULL) ++u; else u = s; if (strcmp(u, "uptime") == 0) return (0); return (-1); } Index: stable/7/usr.bin/w =================================================================== --- stable/7/usr.bin/w (revision 204288) +++ stable/7/usr.bin/w (revision 204289) Property changes on: stable/7/usr.bin/w ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.bin/w:r203688