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