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