Home | History | Annotate | Line # | Download | only in ps
ps.c revision 1.27
      1 /*	$NetBSD: ps.c,v 1.27 1999/03/26 22:36:02 bgrayson Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1990, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\
     39 	The Regents of the University of California.  All rights reserved.\n");
     40 #endif /* not lint */
     41 
     42 #ifndef lint
     43 #if 0
     44 static char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
     45 #else
     46 __RCSID("$NetBSD: ps.c,v 1.27 1999/03/26 22:36:02 bgrayson Exp $");
     47 #endif
     48 #endif /* not lint */
     49 
     50 #include <sys/param.h>
     51 #include <sys/user.h>
     52 #include <sys/time.h>
     53 #include <sys/resource.h>
     54 #include <sys/proc.h>
     55 #include <sys/stat.h>
     56 #include <sys/ioctl.h>
     57 #include <sys/sysctl.h>
     58 
     59 #include <ctype.h>
     60 #include <err.h>
     61 #include <errno.h>
     62 #include <fcntl.h>
     63 #include <kvm.h>
     64 #include <limits.h>
     65 #include <nlist.h>
     66 #include <paths.h>
     67 #include <pwd.h>
     68 #include <stdio.h>
     69 #include <stdlib.h>
     70 #include <string.h>
     71 #include <unistd.h>
     72 
     73 #include "ps.h"
     74 
     75 #ifdef P_PPWAIT
     76 #define NEWVM
     77 #endif
     78 
     79 KINFO *kinfo;
     80 struct varent *vhead, *vtail;
     81 
     82 int	eval;			/* exit value */
     83 int	rawcpu;			/* -C */
     84 int	sumrusage;		/* -S */
     85 int	termwidth;		/* width of screen (0 == infinity) */
     86 int	totwidth;		/* calculated width of requested variables */
     87 
     88 int	needuser, needcomm, needenv, commandonly;
     89 
     90 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
     91 
     92 static char	*kludge_oldps_options __P((char *));
     93 static int	 pscomp __P((const void *, const void *));
     94 static void	 saveuser __P((KINFO *));
     95 static void	 scanvars __P((void));
     96 static void	 usage __P((void));
     97 int		 main __P((int, char *[]));
     98 
     99 char dfmt[] = "pid tt state time command";
    100 char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
    101 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
    102 char   o1[] = "pid";
    103 char   o2[] = "tt state time command";
    104 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
    105 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
    106 
    107 kvm_t *kd;
    108 
    109 int
    110 main(argc, argv)
    111 	int argc;
    112 	char *argv[];
    113 {
    114 	struct kinfo_proc *kp;
    115 	struct varent *vent;
    116 	struct winsize ws;
    117 	gid_t egid = getegid();
    118 	int ch, flag, i, fmt, lineno, nentries;
    119 	int prtheader, wflag, what, xflg;
    120 	char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
    121 	char *ttname;
    122 
    123 	(void)setegid(getgid());
    124 	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
    125 	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
    126 	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
    127 	     ws.ws_col == 0)
    128 		termwidth = 79;
    129 	else
    130 		termwidth = ws.ws_col - 1;
    131 
    132 	if (argc > 1)
    133 		argv[1] = kludge_oldps_options(argv[1]);
    134 
    135 	fmt = prtheader = wflag = xflg = 0;
    136 	what = KERN_PROC_UID;
    137 	flag = getuid();
    138 	memf = nlistf = swapf = NULL;
    139 	while ((ch = getopt(argc, argv,
    140 	    "acCeghjLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
    141 		switch((char)ch) {
    142 		case 'a':
    143 			what = KERN_PROC_ALL;
    144 			flag = 0;
    145 			break;
    146 		case 'c':
    147 			commandonly = 1;
    148 			break;
    149 		case 'e':			/* XXX set ufmt */
    150 			needenv = 1;
    151 			break;
    152 		case 'C':
    153 			rawcpu = 1;
    154 			break;
    155 		case 'g':
    156 			break;			/* no-op */
    157 		case 'h':
    158 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
    159 			break;
    160 		case 'j':
    161 			parsefmt(jfmt);
    162 			fmt = 1;
    163 			jfmt[0] = '\0';
    164 			break;
    165 		case 'L':
    166 			showkey();
    167 			exit(0);
    168 			/* NOTREACHED */
    169 		case 'l':
    170 			parsefmt(lfmt);
    171 			fmt = 1;
    172 			lfmt[0] = '\0';
    173 			break;
    174 		case 'M':
    175 			memf = optarg;
    176 			break;
    177 		case 'm':
    178 			sortby = SORTMEM;
    179 			break;
    180 		case 'N':
    181 			nlistf = optarg;
    182 			break;
    183 		case 'O':
    184 			parsefmt(o1);
    185 			parsefmt(optarg);
    186 			parsefmt(o2);
    187 			o1[0] = o2[0] = '\0';
    188 			fmt = 1;
    189 			break;
    190 		case 'o':
    191 			parsefmt(optarg);
    192 			fmt = 1;
    193 			break;
    194 		case 'p':
    195 			what = KERN_PROC_PID;
    196 			flag = atol(optarg);
    197 			xflg = 1;
    198 			break;
    199 		case 'r':
    200 			sortby = SORTCPU;
    201 			break;
    202 		case 'S':
    203 			sumrusage = 1;
    204 			break;
    205 		case 'T':
    206 			if ((ttname = ttyname(STDIN_FILENO)) == NULL)
    207 				errx(1, "stdin: not a terminal");
    208 			goto tty;
    209 		case 't':
    210 			ttname = optarg;
    211 		tty: {
    212 			struct stat sb;
    213 			char *ttypath, pathbuf[MAXPATHLEN];
    214 
    215 			if (strcmp(ttname, "co") == 0)
    216 				ttypath = _PATH_CONSOLE;
    217 			else if (*ttname != '/')
    218 				(void)snprintf(ttypath = pathbuf,
    219 				    sizeof(pathbuf), "%s%s", _PATH_TTY, ttname);
    220 			else
    221 				ttypath = ttname;
    222 			if (stat(ttypath, &sb) == -1)
    223 				err(1, "%s", ttypath);
    224 			if (!S_ISCHR(sb.st_mode))
    225 				errx(1, "%s: not a terminal", ttypath);
    226 			what = KERN_PROC_TTY;
    227 			flag = sb.st_rdev;
    228 			break;
    229 		}
    230 		case 'U':
    231 			if (*optarg != '\0') {
    232 				struct passwd *pw;
    233 				char *ep;
    234 
    235 				what = KERN_PROC_UID;
    236 				pw = getpwnam(optarg);
    237 				if (pw == NULL) {
    238 					errno = 0;
    239 					flag = strtoul(optarg, &ep, 10);
    240 					if (errno)
    241 						err(1, "%s", optarg);
    242 					if (*ep != '\0')
    243 						errx(1, "%s: illegal user name",
    244 						    optarg);
    245 				} else
    246 					flag = pw->pw_uid;
    247 			}
    248 			break;
    249 		case 'u':
    250 			parsefmt(ufmt);
    251 			sortby = SORTCPU;
    252 			fmt = 1;
    253 			ufmt[0] = '\0';
    254 			break;
    255 		case 'v':
    256 			parsefmt(vfmt);
    257 			sortby = SORTMEM;
    258 			fmt = 1;
    259 			vfmt[0] = '\0';
    260 			break;
    261 		case 'W':
    262 			swapf = optarg;
    263 			break;
    264 		case 'w':
    265 			if (wflag)
    266 				termwidth = UNLIMITED;
    267 			else if (termwidth < 131)
    268 				termwidth = 131;
    269 			wflag++;
    270 			break;
    271 		case 'x':
    272 			xflg = 1;
    273 			break;
    274 		case '?':
    275 		default:
    276 			usage();
    277 		}
    278 	argc -= optind;
    279 	argv += optind;
    280 
    281 #define	BACKWARD_COMPATIBILITY
    282 #ifdef	BACKWARD_COMPATIBILITY
    283 	if (*argv) {
    284 		nlistf = *argv;
    285 		if (*++argv) {
    286 			memf = *argv;
    287 			if (*++argv)
    288 				swapf = *argv;
    289 		}
    290 	}
    291 #endif
    292 	/*
    293 	 * Discard setgid privileges.  If not the running kernel, we toss
    294 	 * them away totally so that bad guys can't print interesting stuff
    295 	 * from kernel memory, otherwise switch back to kmem for the
    296 	 * duration of the kvm_openfiles() call.
    297 	 */
    298 	if (nlistf != NULL || memf != NULL || swapf != NULL)
    299 		(void)setgid(getgid());
    300 	else
    301 		(void)setegid(egid);
    302 
    303 	kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
    304 	if (kd == 0)
    305 		errx(1, "%s", errbuf);
    306 
    307 	if (nlistf == NULL && memf == NULL && swapf == NULL)
    308 		(void)setgid(getgid());
    309 
    310 	if (!fmt)
    311 		parsefmt(dfmt);
    312 
    313 	/*
    314 	 * scan requested variables, noting what structures are needed,
    315 	 * and adjusting header widths as appropiate.
    316 	 */
    317 	scanvars();
    318 	/*
    319 	 * select procs
    320 	 */
    321 	if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
    322 	{
    323 	  	/*  sysctl() ought to provide some sort of
    324 		 *  always-working-but-minimal-functionality
    325 		 *  method of providing at least some of the
    326 		 *  process information.  Unfortunately, such a
    327 		 *  change will require too much work to be put
    328 		 *  into 1.4.  For now, enable this experimental
    329 		 *  /proc-based support instead (if /proc is
    330 		 *  mounted) to grab as much information as we can.
    331 		 *  The guts of emulating kvm_getprocs() is in
    332 		 *  the file procfs_ops.c.  */
    333 		warnx("%s.\n    %s", kvm_geterr(kd),
    334 		    "Attempting experimental, insecure /proc-based method.");
    335 		/*  procfs_getprocs supports all but the
    336 		 *  KERN_PROC_RUID flag.  */
    337 		kp=procfs_getprocs(what, flag, &nentries);
    338 		if (kp == 0) {
    339 		  errx(1, "/proc-based lookup also failed.  Giving up...");
    340 		}
    341 		/*  An intruder could have put an ordinary filesystem
    342 		 *  on /proc, and keep updating it to make
    343 		 *  it look like it's the real /proc, when in
    344 		 *  reality they are hiding information about
    345 		 *  some trojan processes that are running.
    346 		 *  Should we walk the mounted-filesystems table
    347 		 *  to figure out whether /proc is mounted with
    348 		 *  nothing mounted on top of it?  For now, just
    349 		 *  print a verbose warning.  XXX  bgrayson  */
    350 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s",
    351 		    "*****************************************\n",
    352 		    "Warning:  /proc does not provide sufficient ",
    353 		    "information to provide\n",
    354 		    "valid data for all fields.\n",
    355 		    "1.  Several fields (like ",
    356 		    "STAT and TIME) will be incorrect.\n",
    357 		    "2.  If your system may be compromised, ",
    358 		    "verify that /proc is secure\n",
    359 		    "    before trusting these results.\n");
    360 	}
    361 	if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
    362 		err(1, "%s", "");
    363 	for (i = nentries; --i >= 0; ++kp) {
    364 		kinfo[i].ki_p = kp;
    365 		if (needuser)
    366 			saveuser(&kinfo[i]);
    367 	}
    368 	/*
    369 	 * print header
    370 	 */
    371 	printheader();
    372 	if (nentries == 0)
    373 		exit(0);
    374 	/*
    375 	 * sort proc list
    376 	 */
    377 	qsort(kinfo, nentries, sizeof(KINFO), pscomp);
    378 	/*
    379 	 * for each proc, call each variable output function.
    380 	 */
    381 	for (i = lineno = 0; i < nentries; i++) {
    382 		KINFO *ki = &kinfo[i];
    383 
    384 		if (xflg == 0 && (KI_EPROC(ki)->e_tdev == NODEV ||
    385 		    (KI_PROC(ki)->p_flag & P_CONTROLT ) == 0))
    386 			continue;
    387 		for (vent = vhead; vent; vent = vent->next) {
    388 			(vent->var->oproc)(ki, vent);
    389 			if (vent->next != NULL)
    390 				(void)putchar(' ');
    391 		}
    392 		(void)putchar('\n');
    393 		if (prtheader && lineno++ == prtheader - 4) {
    394 			(void)putchar('\n');
    395 			printheader();
    396 			lineno = 0;
    397 		}
    398 	}
    399 	exit(eval);
    400 	/* NOTREACHED */
    401 }
    402 
    403 static void
    404 scanvars()
    405 {
    406 	struct varent *vent;
    407 	VAR *v;
    408 	int i;
    409 
    410 	for (vent = vhead; vent; vent = vent->next) {
    411 		v = vent->var;
    412 		i = strlen(v->header);
    413 		if (v->width < i)
    414 			v->width = i;
    415 		totwidth += v->width + 1;	/* +1 for space */
    416 		if (v->flag & USER)
    417 			needuser = 1;
    418 		if (v->flag & COMM)
    419 			needcomm = 1;
    420 	}
    421 	totwidth--;
    422 }
    423 
    424 static void
    425 saveuser(ki)
    426 	KINFO *ki;
    427 {
    428 	struct pstats pstats;
    429 	struct usave *usp;
    430 
    431 	usp = &ki->ki_u;
    432 	if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats,
    433 	    (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) {
    434 		/*
    435 		 * The u-area might be swapped out, and we can't get
    436 		 * at it because we have a crashdump and no swap.
    437 		 * If it's here fill in these fields, otherwise, just
    438 		 * leave them 0.
    439 		 */
    440 		usp->u_start = pstats.p_start;
    441 		usp->u_ru = pstats.p_ru;
    442 		usp->u_cru = pstats.p_cru;
    443 		usp->u_valid = 1;
    444 	} else
    445 		usp->u_valid = 0;
    446 }
    447 
    448 static int
    449 pscomp(a, b)
    450 	const void *a, *b;
    451 {
    452 	int i;
    453 #ifdef NEWVM
    454 #define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \
    455 		  KI_EPROC(k)->e_vm.vm_tsize)
    456 #else
    457 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize)
    458 #endif
    459 
    460 	if (sortby == SORTCPU)
    461 		return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a));
    462 	if (sortby == SORTMEM)
    463 		return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a));
    464 	i =  KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev;
    465 	if (i == 0)
    466 		i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid;
    467 	return (i);
    468 }
    469 
    470 /*
    471  * ICK (all for getopt), would rather hide the ugliness
    472  * here than taint the main code.
    473  *
    474  *  ps foo -> ps -foo
    475  *  ps 34 -> ps -p34
    476  *
    477  * The old convention that 't' with no trailing tty arg means the user's
    478  * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
    479  * feature is available with the option 'T', which takes no argument.
    480  */
    481 static char *
    482 kludge_oldps_options(s)
    483 	char *s;
    484 {
    485 	size_t len;
    486 	char *newopts, *ns, *cp;
    487 
    488 	len = strlen(s);
    489 	if ((newopts = ns = malloc(len + 3)) == NULL)
    490 		err(1, "%s", "");
    491 	/*
    492 	 * options begin with '-'
    493 	 */
    494 	if (*s != '-')
    495 		*ns++ = '-';	/* add option flag */
    496 	/*
    497 	 * gaze to end of argv[1]
    498 	 */
    499 	cp = s + len - 1;
    500 	/*
    501 	 * if last letter is a 't' flag with no argument (in the context
    502 	 * of the oldps options -- option string NOT starting with a '-' --
    503 	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
    504 	 */
    505 	if (*cp == 't' && *s != '-')
    506 		*cp = 'T';
    507 	else {
    508 		/*
    509 		 * otherwise check for trailing number, which *may* be a
    510 		 * pid.
    511 		 */
    512 		while (cp >= s && isdigit(*cp))
    513 			--cp;
    514 	}
    515 	cp++;
    516 	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
    517 	ns += cp - s;
    518 	/*
    519 	 * if there's a trailing number, and not a preceding 'p' (pid) or
    520 	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
    521 	 */
    522 	if (isdigit(*cp) &&
    523 	    (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' &&
    524 	     (cp - 1 == s || cp[-2] != 't'))))
    525 		*ns++ = 'p';
    526 	/* and append the number */
    527 	(void)strcpy(ns, cp);		/* XXX strcpy is safe */
    528 
    529 	return (newopts);
    530 }
    531 
    532 static void
    533 usage()
    534 {
    535 
    536 	(void)fprintf(stderr,
    537 	    "usage:\t%s\n\t   %s\n\t%s\n",
    538 	    "ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]",
    539 	    "[-M core] [-N system] [-W swap]",
    540 	    "ps [-L]");
    541 	exit(1);
    542 	/* NOTREACHED */
    543 }
    544