1 /* $OpenBSD: ps.c,v 1.46 2008/07/18 14:24:18 kevlo Exp $ */ 2 /* $NetBSD: ps.c,v 1.15 1995/05/18 20:33:25 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 static char copyright[] = 35 "@(#) Copyright (c) 1990, 1993, 1994\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 42 #else 43 static char rcsid[] = "$OpenBSD: ps.c,v 1.46 2008/07/18 14:24:18 kevlo Exp $"; 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/user.h> 49 #include <sys/time.h> 50 #include <sys/resource.h> 51 #include <sys/proc.h> 52 #include <sys/stat.h> 53 #include <sys/ioctl.h> 54 #include <sys/sysctl.h> 55 #include <sys/types.h> 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <kvm.h> 62 #include <nlist.h> 63 #include <paths.h> 64 #include <pwd.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 #include <limits.h> 70 71 #include "ps.h" 72 73 extern char *__progname; 74 75 struct varent *vhead; 76 77 int eval; /* exit value */ 78 int rawcpu; /* -C */ 79 int sumrusage; /* -S */ 80 int termwidth; /* width of screen (0 == infinity) */ 81 int totwidth; /* calculated width of requested variables */ 82 83 int ncpu = 1; 84 85 int needcomm, needenv, neednlist, commandonly; 86 87 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 88 89 static char *kludge_oldps_options(char *); 90 static int pscomp(const void *, const void *); 91 static void scanvars(void); 92 static void usage(void); 93 94 char dfmt[] = "pid tt state time command"; 95 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 96 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 97 char o1[] = "pid"; 98 char o2[] = "tt state time command"; 99 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 100 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 101 102 kvm_t *kd; 103 int kvm_sysctl_only; 104 105 int 106 main(int argc, char *argv[]) 107 { 108 struct kinfo_proc2 *kp, **kinfo; 109 struct varent *vent; 110 struct winsize ws; 111 struct passwd *pwd; 112 dev_t ttydev; 113 pid_t pid; 114 uid_t uid; 115 int all, ch, flag, i, fmt, lineno, nentries, mib[6]; 116 int prtheader, wflag, kflag, what, Uflag, xflg; 117 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 118 size_t size; 119 120 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 121 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 122 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || 123 ws.ws_col == 0) 124 termwidth = 79; 125 else 126 termwidth = ws.ws_col - 1; 127 128 if (argc > 1) 129 argv[1] = kludge_oldps_options(argv[1]); 130 131 all = fmt = prtheader = wflag = kflag = Uflag = xflg = 0; 132 pid = -1; 133 uid = 0; 134 ttydev = NODEV; 135 memf = nlistf = swapf = NULL; 136 while ((ch = getopt(argc, argv, 137 "acCeghjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) 138 switch (ch) { 139 case 'a': 140 all = 1; 141 break; 142 case 'c': 143 commandonly = 1; 144 break; 145 case 'e': /* XXX set ufmt */ 146 needenv = 1; 147 break; 148 case 'C': 149 rawcpu = 1; 150 break; 151 case 'g': 152 break; /* no-op */ 153 case 'h': 154 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 155 break; 156 case 'j': 157 parsefmt(jfmt); 158 fmt = 1; 159 jfmt[0] = '\0'; 160 break; 161 case 'k': 162 kflag++; 163 break; 164 case 'L': 165 showkey(); 166 exit(0); 167 case 'l': 168 parsefmt(lfmt); 169 fmt = 1; 170 lfmt[0] = '\0'; 171 break; 172 case 'M': 173 memf = optarg; 174 break; 175 case 'm': 176 sortby = SORTMEM; 177 break; 178 case 'N': 179 nlistf = optarg; 180 break; 181 case 'O': 182 parsefmt(o1); 183 parsefmt(optarg); 184 parsefmt(o2); 185 o1[0] = o2[0] = '\0'; 186 fmt = 1; 187 break; 188 case 'o': 189 parsefmt(optarg); 190 fmt = 1; 191 break; 192 case 'p': 193 pid = atol(optarg); 194 xflg = 1; 195 break; 196 case 'r': 197 sortby = SORTCPU; 198 break; 199 case 'S': 200 sumrusage = 1; 201 break; 202 case 'T': 203 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 204 errx(1, "stdin: not a terminal"); 205 /* FALLTHROUGH */ 206 case 't': { 207 struct stat sb; 208 char *ttypath, pathbuf[MAXPATHLEN]; 209 210 if (strcmp(optarg, "co") == 0) 211 ttypath = _PATH_CONSOLE; 212 else if (*optarg != '/') 213 (void)snprintf(ttypath = pathbuf, 214 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); 215 else 216 ttypath = optarg; 217 if (stat(ttypath, &sb) == -1) 218 err(1, "%s", ttypath); 219 if (!S_ISCHR(sb.st_mode)) 220 errx(1, "%s: not a terminal", ttypath); 221 ttydev = sb.st_rdev; 222 break; 223 } 224 case 'U': 225 pwd = getpwnam(optarg); 226 if (pwd == NULL) 227 errx(1, "%s: no such user", optarg); 228 uid = pwd->pw_uid; 229 endpwent(); 230 Uflag = xflg = 1; 231 break; 232 case 'u': 233 parsefmt(ufmt); 234 sortby = SORTCPU; 235 fmt = 1; 236 ufmt[0] = '\0'; 237 break; 238 case 'v': 239 parsefmt(vfmt); 240 sortby = SORTMEM; 241 fmt = 1; 242 vfmt[0] = '\0'; 243 break; 244 case 'W': 245 swapf = optarg; 246 break; 247 case 'w': 248 if (wflag) 249 termwidth = UNLIMITED; 250 else if (termwidth < 131) 251 termwidth = 131; 252 wflag++; 253 break; 254 case 'x': 255 xflg = 1; 256 break; 257 default: 258 usage(); 259 } 260 argc -= optind; 261 argv += optind; 262 263 #define BACKWARD_COMPATIBILITY 264 #ifdef BACKWARD_COMPATIBILITY 265 if (*argv) { 266 nlistf = *argv; 267 if (*++argv) { 268 memf = *argv; 269 if (*++argv) 270 swapf = *argv; 271 } 272 } 273 #endif 274 275 if (nlistf == NULL && memf == NULL && swapf == NULL) { 276 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 277 kvm_sysctl_only = 1; 278 } else { 279 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 280 } 281 if (kd == NULL && (nlistf != NULL || memf != NULL || swapf != NULL)) 282 errx(1, "%s", errbuf); 283 284 if (!fmt) 285 parsefmt(dfmt); 286 287 /* XXX - should be cleaner */ 288 if (!all && ttydev == NODEV && pid == -1 && !Uflag) { 289 uid = getuid(); 290 Uflag = 1; 291 } 292 293 /* 294 * scan requested variables, noting what structures are needed, 295 * and adjusting header widths as appropriate. 296 */ 297 scanvars(); 298 299 if (neednlist && !nlistread) 300 (void) donlist(); 301 302 /* 303 * get proc list 304 */ 305 if (Uflag) { 306 what = KERN_PROC_UID; 307 flag = uid; 308 } else if (ttydev != NODEV) { 309 what = KERN_PROC_TTY; 310 flag = ttydev; 311 } else if (pid != -1) { 312 what = KERN_PROC_PID; 313 flag = pid; 314 } else if (kflag) { 315 what = KERN_PROC_KTHREAD; 316 flag = 0; 317 } else { 318 what = KERN_PROC_ALL; 319 flag = 0; 320 } 321 322 mib[0] = CTL_HW; 323 mib[1] = HW_NCPU; 324 size = sizeof(ncpu); 325 (void) sysctl(mib, 2, &ncpu, &size, NULL, 0); 326 327 /* 328 * select procs 329 */ 330 if (kd != NULL) { 331 kp = kvm_getproc2(kd, what, flag, sizeof(*kp), &nentries); 332 if (kp == NULL) 333 errx(1, "%s", kvm_geterr(kd)); 334 } else { 335 mib[0] = CTL_KERN; 336 mib[1] = KERN_PROC2; 337 mib[2] = what; 338 mib[3] = flag; 339 mib[4] = sizeof(struct kinfo_proc2); 340 mib[5] = 0; 341 retry: 342 if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) 343 err(1, "could not get kern.proc2 size"); 344 size = 5 * size / 4; /* extra slop */ 345 if ((kp = malloc(size)) == NULL) 346 err(1, 347 "failed to allocate memory for proc structures"); 348 mib[5] = (int)(size / sizeof(struct kinfo_proc2)); 349 if (sysctl(mib, 6, kp, &size, NULL, 0) < 0) { 350 if (errno == ENOMEM) { 351 free(kp); 352 goto retry; 353 } 354 err(1, "could not read kern.proc2"); 355 } 356 nentries = (int)(size / sizeof(struct kinfo_proc2)); 357 } 358 /* 359 * print header 360 */ 361 printheader(); 362 if (nentries == 0) 363 exit(1); 364 /* 365 * sort proc list, we convert from an array of structs to an array 366 * of pointers to make the sort cheaper. 367 */ 368 if ((kinfo = calloc(sizeof(*kinfo), nentries)) == NULL) 369 err(1, "failed to allocate memory for proc pointers"); 370 for (i = 0; i < nentries; i++) 371 kinfo[i] = &kp[i]; 372 qsort(kinfo, nentries, sizeof(*kinfo), pscomp); 373 /* 374 * for each proc, call each variable output function. 375 */ 376 for (i = lineno = 0; i < nentries; i++) { 377 if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV || 378 (kinfo[i]->p_flag & P_CONTROLT ) == 0)) 379 continue; 380 for (vent = vhead; vent; vent = vent->next) { 381 (vent->var->oproc)(kinfo[i], vent); 382 if (vent->next != NULL) 383 (void)putchar(' '); 384 } 385 (void)putchar('\n'); 386 if (prtheader && lineno++ == prtheader - 4) { 387 (void)putchar('\n'); 388 printheader(); 389 lineno = 0; 390 } 391 } 392 exit(eval); 393 } 394 395 static void 396 scanvars(void) 397 { 398 struct varent *vent; 399 VAR *v; 400 int i; 401 402 for (vent = vhead; vent; vent = vent->next) { 403 v = vent->var; 404 i = strlen(v->header); 405 if (v->width < i) 406 v->width = i; 407 totwidth += v->width + 1; /* +1 for space */ 408 if (v->flag & COMM) 409 needcomm = 1; 410 if (v->flag & NLIST) 411 neednlist = 1; 412 } 413 totwidth--; 414 } 415 416 static int 417 pscomp(const void *v1, const void *v2) 418 { 419 const struct kinfo_proc2 *kp1 = *(const struct kinfo_proc2 **)v1; 420 const struct kinfo_proc2 *kp2 = *(const struct kinfo_proc2 **)v2; 421 int i; 422 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize) 423 424 if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0) 425 return (i); 426 if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0) 427 return (i); 428 if ((i = kp1->p_tdev - kp2->p_tdev) == 0 && 429 (i = kp1->p_ustart_sec - kp2->p_ustart_sec) == 0) 430 i = kp1->p_ustart_usec - kp2->p_ustart_usec; 431 return (i); 432 } 433 434 /* 435 * ICK (all for getopt), would rather hide the ugliness 436 * here than taint the main code. 437 * 438 * ps foo -> ps -foo 439 * ps 34 -> ps -p34 440 * 441 * The old convention that 't' with no trailing tty arg means the users 442 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 443 * feature is available with the option 'T', which takes no argument. 444 */ 445 static char * 446 kludge_oldps_options(char *s) 447 { 448 size_t len; 449 char *newopts, *ns, *cp; 450 451 len = strlen(s); 452 if ((newopts = ns = malloc(2 + len + 1)) == NULL) 453 err(1, NULL); 454 /* 455 * options begin with '-' 456 */ 457 if (*s != '-') 458 *ns++ = '-'; /* add option flag */ 459 460 /* 461 * gaze to end of argv[1] 462 */ 463 cp = s + len - 1; 464 /* 465 * if last letter is a 't' flag with no argument (in the context 466 * of the oldps options -- option string NOT starting with a '-' -- 467 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 468 */ 469 if (*cp == 't' && *s != '-') 470 *cp = 'T'; 471 else { 472 /* 473 * otherwise check for trailing number, which *may* be a 474 * pid. 475 */ 476 while (cp >= s && isdigit(*cp)) 477 --cp; 478 } 479 cp++; 480 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 481 ns += cp - s; 482 /* 483 * if there's a trailing number, and not a preceding 'p' (pid) or 484 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 485 */ 486 if (isdigit(*cp) && (cp == s || (cp[-1] != 't' && cp[-1] != 'p' && 487 (cp - 1 == s || cp[-2] != 't')))) 488 *ns++ = 'p'; 489 /* and append the number */ 490 (void)strlcpy(ns, cp, newopts + len + 3 - ns); 491 492 return (newopts); 493 } 494 495 static void 496 usage(void) 497 { 498 (void)fprintf(stderr, 499 "usage: %s [-aCcehjkLlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n", 500 __progname); 501 (void)fprintf(stderr, 502 "%-*s[-t tty] [-U username] [-W swap]\n", (int)strlen(__progname) + 8, ""); 503 exit(1); 504 } 505