1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * 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 34 #ifndef lint 35 char copyright[] = 36 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ 37 All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char sccsid[] = "@(#)ps.c 5.43 (Berkeley) 7/1/91"; 42 static char rcsid[] = "$Header: /cvsroot/src/bin/ps/ps.c,v 1.7 1993/07/19 11:02:10 cgd Exp $"; 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/user.h> 47 #include <sys/time.h> 48 #include <sys/resource.h> 49 #include <sys/proc.h> 50 #include <sys/stat.h> 51 #include <sys/ioctl.h> 52 #include <sys/kinfo.h> 53 #include <nlist.h> 54 #include <kvm.h> 55 #include <errno.h> 56 #include <unistd.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <paths.h> 61 #include "ps.h" 62 63 #ifdef SPPWAIT 64 #define NEWVM 65 #endif 66 67 KINFO *kinfo; 68 struct varent *vhead, *vtail; 69 70 int eval; /* exit value */ 71 int rawcpu; /* -C */ 72 int sumrusage; /* -S */ 73 int termwidth; /* width of screen (0 == infinity) */ 74 int totwidth; /* calculated width of requested variables */ 75 76 static int needuser, needcomm, needenv; 77 78 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 79 80 uid_t getuid(); 81 char *ttyname(); 82 double getpcpu(); /* 14 Sep 92*/ 83 84 char dfmt[] = "pid tt state time command"; 85 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 86 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 87 char o1[] = "pid"; 88 char o2[] = "tt state time command"; 89 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 90 char vfmt[] = 91 "pid state time sl re pagein vsz rss lim tsiz trs %cpu %mem command"; 92 93 main(argc, argv) 94 int argc; 95 char **argv; 96 { 97 extern char *optarg; 98 extern int optind; 99 register struct proc *p; 100 register size_t nentries; 101 register struct varent *vent; 102 register int i; 103 struct winsize ws; 104 dev_t ttydev; 105 int all, ch, flag, fmt, lineno, pid, prtheader, uid, wflag, what, xflg; 106 int pscomp(); 107 char *nlistf, *memf, *swapf; 108 char *kludge_oldps_options(); 109 110 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 111 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 112 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 113 ws.ws_col == 0) 114 termwidth = 79; 115 else 116 termwidth = ws.ws_col - 1; 117 118 if (argc > 1) 119 argv[1] = kludge_oldps_options(argv[1]); 120 121 fmt = 0; 122 all = wflag = xflg = 0; 123 pid = uid = -1; 124 ttydev = NODEV; 125 memf = nlistf = swapf = NULL; 126 while ((ch = getopt(argc, argv, 127 "aCeghjLlM:mN:O:o:p:rSTt:uvW:wx")) != EOF) 128 switch((char)ch) { 129 case 'a': 130 all = 1; 131 break; 132 case 'C': 133 rawcpu = 1; 134 break; 135 case 'e': 136 needenv = 1; 137 break; 138 case 'g': 139 break; /* no-op */ 140 case 'h': 141 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 142 break; 143 case 'j': 144 parsefmt(jfmt); 145 fmt = 1; 146 jfmt[0] = '\0'; 147 break; 148 case 'L': 149 showkey(); 150 exit(0); 151 case 'l': 152 parsefmt(lfmt); 153 fmt = 1; 154 lfmt[0] = '\0'; 155 break; 156 case 'M': 157 memf = optarg; 158 break; 159 case 'm': 160 sortby = SORTMEM; 161 break; 162 case 'N': 163 nlistf = optarg; 164 break; 165 case 'O': 166 parsefmt(o1); 167 parsefmt(optarg); 168 parsefmt(o2); 169 o1[0] = o2[0] = '\0'; 170 fmt = 1; 171 break; 172 case 'o': 173 parsefmt(optarg); 174 fmt = 1; 175 break; 176 case 'p': 177 pid = atoi(optarg); 178 xflg = 1; 179 break; 180 case 'r': 181 sortby = SORTCPU; 182 break; 183 case 'S': 184 sumrusage = 1; 185 break; 186 case 'T': 187 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 188 err("stdin: not a terminal"); 189 /* FALLTHROUGH */ 190 case 't': { 191 char *ttypath; 192 struct stat stbuf; 193 char pathbuf[MAXPATHLEN]; 194 195 if (strcmp(optarg, "co") == 0) 196 ttypath = _PATH_CONSOLE; 197 else if (*optarg != '/') 198 (void) sprintf(ttypath = pathbuf, "%s%s", 199 _PATH_TTY, optarg); 200 else 201 ttypath = optarg; 202 if (stat(ttypath, &stbuf) == -1) 203 err("%s: %s", ttypath, strerror(errno)); 204 if (!S_ISCHR(stbuf.st_mode)) 205 err("%s: not a terminal", ttypath); 206 ttydev = stbuf.st_rdev; 207 break; 208 } 209 case 'u': 210 parsefmt(ufmt); 211 sortby = SORTCPU; 212 fmt = 1; 213 ufmt[0] = '\0'; 214 break; 215 case 'v': 216 parsefmt(vfmt); 217 sortby = SORTMEM; 218 fmt = 1; 219 vfmt[0] = '\0'; 220 break; 221 case 'W': 222 swapf = optarg; 223 break; 224 case 'w': 225 if (wflag) 226 termwidth = UNLIMITED; 227 else if (termwidth < 131) 228 termwidth = 131; 229 wflag++; 230 break; 231 case 'x': 232 xflg = 1; 233 break; 234 case '?': 235 default: 236 usage(); 237 } 238 argc -= optind; 239 argv += optind; 240 241 #define BACKWARD_COMPATIBILITY 242 #ifdef BACKWARD_COMPATIBILITY 243 if (*argv) { 244 245 nlistf = *argv; 246 if (*++argv) { 247 memf = *argv; 248 if (*++argv) 249 swapf = *argv; 250 } 251 } 252 #endif 253 if (kvm_openfiles(nlistf, memf, swapf) == -1) 254 err("kvm_openfiles: %s", kvm_geterr()); 255 256 if (!fmt) 257 parsefmt(dfmt); 258 259 if (!all && ttydev == NODEV && pid == -1) /* XXX - should be cleaner */ 260 uid = getuid(); 261 262 /* 263 * scan requested variables, noting what structures are needed, 264 * and adjusting header widths as appropiate. 265 */ 266 scanvars(); 267 /* 268 * get proc list 269 */ 270 if (uid != -1) { 271 what = KINFO_PROC_UID; 272 flag = uid; 273 } else if (ttydev != NODEV) { 274 what = KINFO_PROC_TTY; 275 flag = ttydev; 276 } else if (pid != -1) { 277 what = KINFO_PROC_PID; 278 flag = pid; 279 } else 280 what = KINFO_PROC_ALL; 281 /* 282 * select procs 283 */ 284 if ((nentries = kvm_getprocs(what, flag)) == -1) 285 err("%s", kvm_geterr()); 286 kinfo = malloc(nentries * sizeof(KINFO)); 287 if (kinfo == NULL) 288 err("%s", strerror(errno)); 289 for (nentries = 0; p = kvm_nextproc(); ++nentries) { 290 kinfo[nentries].ki_p = p; 291 kinfo[nentries].ki_e = kvm_geteproc(p); 292 if (needuser || needcomm) 293 saveuser(&kinfo[nentries]); 294 } 295 /* 296 * print header 297 */ 298 printheader(); 299 if (nentries == 0) 300 exit(0); 301 /* 302 * sort proc list 303 */ 304 qsort((void *)kinfo, nentries, sizeof(KINFO), pscomp); 305 /* 306 * for each proc, call each variable output function. 307 */ 308 for (i = lineno = 0; i < nentries; i++) { 309 if (xflg == 0 && (kinfo[i].ki_e->e_tdev == NODEV || 310 (kinfo[i].ki_p->p_flag & SCTTY ) == 0)) 311 continue; 312 for (vent = vhead; vent; vent = vent->next) { 313 (*vent->var->oproc)(&kinfo[i], vent->var, vent->next); 314 if (vent->next != NULL) 315 (void) putchar(' '); 316 } 317 (void) putchar('\n'); 318 if (prtheader && lineno++ == prtheader-4) { 319 (void) putchar('\n'); 320 printheader(); 321 lineno = 0; 322 } 323 } 324 exit(eval); 325 } 326 327 scanvars() 328 { 329 register struct varent *vent; 330 register VAR *v; 331 register int i; 332 333 for (vent = vhead; vent; vent = vent->next) { 334 v = vent->var; 335 i = strlen(v->header); 336 if (v->width < i) 337 v->width = i; 338 totwidth += v->width + 1; /* +1 for space */ 339 if (v->flag & USER) 340 needuser = 1; 341 if (v->flag & COMM) 342 needcomm = 1; 343 } 344 totwidth--; 345 } 346 347 348 /* XXX - redo */ 349 saveuser(ki) 350 KINFO *ki; 351 { 352 register struct usave *usp; 353 register struct user *up; 354 355 if ((usp = calloc(1, sizeof(struct usave))) == NULL) 356 err("%s", strerror(errno)); 357 up = kvm_getu(ki->ki_p); 358 /* 359 * save arguments and environment if needed 360 */ 361 ki->ki_args = needcomm ? strdup(kvm_getargs(ki->ki_p, up)) : NULL; 362 ki->ki_env = needenv ? strdup(kvm_getenv(ki->ki_p, up)) : NULL; 363 364 if (up != NULL) { 365 ki->ki_u = usp; 366 /* 367 * save important fields 368 */ 369 #ifdef NEWVM 370 usp->u_start = up->u_stats.p_start; 371 usp->u_ru = up->u_stats.p_ru; 372 usp->u_cru = up->u_stats.p_cru; 373 #else 374 usp->u_procp = up->u_procp; 375 usp->u_start = up->u_start; 376 usp->u_ru = up->u_ru; 377 usp->u_cru = up->u_cru; 378 usp->u_acflag = up->u_acflag; 379 #endif 380 } else 381 free(usp); 382 } 383 384 pscomp(k1, k2) 385 KINFO *k1, *k2; 386 { 387 int i; 388 #ifdef NEWVM 389 #define VSIZE(k) ((k)->ki_e->e_vm.vm_dsize + (k)->ki_e->e_vm.vm_ssize + \ 390 (k)->ki_e->e_vm.vm_tsize) 391 #else 392 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) 393 #endif 394 395 if (sortby == SORTCPU) 396 return (getpcpu(k2) - getpcpu(k1)); 397 if (sortby == SORTMEM) 398 return (VSIZE(k2) - VSIZE(k1)); 399 i = k1->ki_e->e_tdev - k2->ki_e->e_tdev; 400 if (i == 0) 401 i = k1->ki_p->p_pid - k2->ki_p->p_pid; 402 return (i); 403 } 404 405 /* 406 * ICK (all for getopt), would rather hide the ugliness 407 * here than taint the main code. 408 * 409 * ps foo -> ps -foo 410 * ps 34 -> ps -p34 411 * 412 * The old convention that 't' with no trailing tty arg means the users 413 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 414 * feature is available with the option 'T', which takes no argument. 415 */ 416 char * 417 kludge_oldps_options(s) 418 char *s; 419 { 420 size_t len; 421 char *newopts, *ns, *cp; 422 423 len = strlen(s); 424 if ((newopts = ns = malloc(len + 2)) == NULL) 425 err("%s", strerror(errno)); 426 /* 427 * options begin with '-' 428 */ 429 if (*s != '-') 430 *ns++ = '-'; /* add option flag */ 431 /* 432 * gaze to end of argv[1] 433 */ 434 cp = s + len - 1; 435 /* 436 * if last letter is a 't' flag with no argument (in the context 437 * of the oldps options -- option string NOT starting with a '-' -- 438 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 439 */ 440 if (*cp == 't' && *s != '-') 441 *cp = 'T'; 442 else { 443 /* 444 * otherwise check for trailing number, which *may* be a 445 * pid. 446 */ 447 while (cp >= s && isdigit(*cp)) 448 --cp; 449 } 450 cp++; 451 bcopy(s, ns, (size_t)(cp - s)); /* copy up to trailing number */ 452 ns += cp - s; 453 /* 454 * if there's a trailing number, and not a preceding 'p' (pid) or 455 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 456 */ 457 if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' && 458 (cp - 1 == s || cp[-2] != 't'))) 459 *ns++ = 'p'; 460 (void) strcpy(ns, cp); /* and append the number */ 461 462 return (newopts); 463 } 464 465 #if __STDC__ 466 #include <stdarg.h> 467 #else 468 #include <varargs.h> 469 #endif 470 471 void 472 #if __STDC__ 473 err(const char *fmt, ...) 474 #else 475 err(fmt, va_alist) 476 char *fmt; 477 va_dcl 478 #endif 479 { 480 va_list ap; 481 #if __STDC__ 482 va_start(ap, fmt); 483 #else 484 va_start(ap); 485 #endif 486 (void)fprintf(stderr, "ps: "); 487 (void)vfprintf(stderr, fmt, ap); 488 va_end(ap); 489 (void)fprintf(stderr, "\n"); 490 exit(1); 491 /* NOTREACHED */ 492 } 493 494 usage() 495 { 496 (void) fprintf(stderr, 497 "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]\n\t [-M core] [-N system] [-W swap]\n ps [-L]\n"); 498 exit(1); 499 } 500