1 /* $OpenBSD: ps.c,v 1.62 2014/07/08 23:31:22 deraadt 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 #include <sys/param.h> 34 #include <sys/sysctl.h> 35 #include <sys/time.h> 36 #include <sys/resource.h> 37 #include <sys/proc.h> 38 #include <sys/stat.h> 39 #include <sys/ioctl.h> 40 41 #include <ctype.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <kvm.h> 46 #include <nlist.h> 47 #include <paths.h> 48 #include <pwd.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <limits.h> 54 55 #include "ps.h" 56 57 extern char *__progname; 58 59 struct varent *vhead; 60 61 int eval; /* exit value */ 62 int rawcpu; /* -C */ 63 int sumrusage; /* -S */ 64 int termwidth; /* width of screen (0 == infinity) */ 65 int totwidth; /* calculated width of requested variables */ 66 67 int needcomm, needenv, neednlist, commandonly; 68 69 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 70 71 static char *kludge_oldps_options(char *); 72 static int pscomp(const void *, const void *); 73 static void scanvars(void); 74 static void usage(void); 75 76 char dfmt[] = "pid tt state time command"; 77 char tfmt[] = "pid tid tt state time command"; 78 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 79 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 80 char o1[] = "pid"; 81 char o2[] = "tt state time command"; 82 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 83 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 84 85 kvm_t *kd; 86 int kvm_sysctl_only; 87 88 int 89 main(int argc, char *argv[]) 90 { 91 struct kinfo_proc *kp, **kinfo; 92 struct varent *vent; 93 struct winsize ws; 94 struct passwd *pwd; 95 dev_t ttydev; 96 pid_t pid; 97 uid_t uid; 98 int all, ch, flag, i, fmt, lineno, nentries; 99 int prtheader, showthreads, wflag, kflag, what, Uflag, xflg; 100 char *nlistf, *memf, *swapf, *cols, errbuf[_POSIX2_LINE_MAX]; 101 102 if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { 103 const char *errstr; 104 105 termwidth = strtonum(cols, 1, INT_MAX, &errstr); 106 if (errstr != NULL) 107 warnx("COLUMNS: %s: %s", cols, errstr); 108 } 109 if (termwidth == 0) { 110 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 111 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 112 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || 113 ws.ws_col == 0) 114 termwidth = 79; 115 else 116 termwidth = ws.ws_col - 1; 117 } 118 119 if (argc > 1) 120 argv[1] = kludge_oldps_options(argv[1]); 121 122 all = fmt = prtheader = showthreads = wflag = kflag = Uflag = xflg = 0; 123 pid = -1; 124 uid = 0; 125 ttydev = NODEV; 126 memf = nlistf = swapf = NULL; 127 while ((ch = getopt(argc, argv, 128 "AaCcegHhjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) 129 switch (ch) { 130 case 'A': 131 all = 1; 132 xflg = 1; 133 break; 134 case 'a': 135 all = 1; 136 break; 137 case 'C': 138 rawcpu = 1; 139 break; 140 case 'c': 141 commandonly = 1; 142 break; 143 case 'e': /* XXX set ufmt */ 144 needenv = 1; 145 break; 146 case 'g': 147 break; /* no-op */ 148 case 'H': 149 showthreads = 1; 150 break; 151 case 'h': 152 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 153 break; 154 case 'j': 155 parsefmt(jfmt); 156 fmt = 1; 157 jfmt[0] = '\0'; 158 break; 159 case 'k': 160 kflag++; 161 break; 162 case 'L': 163 showkey(); 164 exit(0); 165 case 'l': 166 parsefmt(lfmt); 167 fmt = 1; 168 lfmt[0] = '\0'; 169 break; 170 case 'M': 171 memf = optarg; 172 break; 173 case 'm': 174 sortby = SORTMEM; 175 break; 176 case 'N': 177 nlistf = optarg; 178 break; 179 case 'O': 180 parsefmt(o1); 181 parsefmt(optarg); 182 parsefmt(o2); 183 o1[0] = o2[0] = '\0'; 184 fmt = 1; 185 break; 186 case 'o': 187 parsefmt(optarg); 188 fmt = 1; 189 break; 190 case 'p': 191 pid = atol(optarg); 192 xflg = 1; 193 break; 194 case 'r': 195 sortby = SORTCPU; 196 break; 197 case 'S': 198 sumrusage = 1; 199 break; 200 case 'T': 201 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 202 errx(1, "stdin: not a terminal"); 203 /* FALLTHROUGH */ 204 case 't': { 205 struct stat sb; 206 char *ttypath, pathbuf[MAXPATHLEN]; 207 208 if (strcmp(optarg, "co") == 0) 209 ttypath = _PATH_CONSOLE; 210 else if (*optarg != '/') 211 (void)snprintf(ttypath = pathbuf, 212 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); 213 else 214 ttypath = optarg; 215 if (stat(ttypath, &sb) == -1) 216 err(1, "%s", ttypath); 217 if (!S_ISCHR(sb.st_mode)) 218 errx(1, "%s: not a terminal", ttypath); 219 ttydev = sb.st_rdev; 220 break; 221 } 222 case 'U': 223 pwd = getpwnam(optarg); 224 if (pwd == NULL) 225 errx(1, "%s: no such user", optarg); 226 uid = pwd->pw_uid; 227 endpwent(); 228 Uflag = xflg = 1; 229 break; 230 case 'u': 231 parsefmt(ufmt); 232 sortby = SORTCPU; 233 fmt = 1; 234 ufmt[0] = '\0'; 235 break; 236 case 'v': 237 parsefmt(vfmt); 238 sortby = SORTMEM; 239 fmt = 1; 240 vfmt[0] = '\0'; 241 break; 242 case 'W': 243 swapf = optarg; 244 break; 245 case 'w': 246 if (wflag) 247 termwidth = UNLIMITED; 248 else if (termwidth < 131) 249 termwidth = 131; 250 wflag++; 251 break; 252 case 'x': 253 xflg = 1; 254 break; 255 default: 256 usage(); 257 } 258 argc -= optind; 259 argv += optind; 260 261 #define BACKWARD_COMPATIBILITY 262 #ifdef BACKWARD_COMPATIBILITY 263 if (*argv) { 264 nlistf = *argv; 265 if (*++argv) { 266 memf = *argv; 267 if (*++argv) 268 swapf = *argv; 269 } 270 } 271 #endif 272 273 if (nlistf == NULL && memf == NULL && swapf == NULL) { 274 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 275 kvm_sysctl_only = 1; 276 } else { 277 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 278 } 279 if (kd == NULL) 280 errx(1, "%s", errbuf); 281 282 if (!fmt) { 283 if (showthreads) 284 parsefmt(tfmt); 285 else 286 parsefmt(dfmt); 287 } 288 289 /* XXX - should be cleaner */ 290 if (!all && ttydev == NODEV && pid == -1 && !Uflag) { 291 uid = getuid(); 292 Uflag = 1; 293 } 294 295 /* 296 * scan requested variables, noting what structures are needed, 297 * and adjusting header widths as appropriate. 298 */ 299 scanvars(); 300 301 if (neednlist && !nlistread) 302 (void) donlist(); 303 304 /* 305 * get proc list 306 */ 307 if (Uflag) { 308 what = KERN_PROC_UID; 309 flag = uid; 310 } else if (ttydev != NODEV) { 311 what = KERN_PROC_TTY; 312 flag = ttydev; 313 } else if (pid != -1) { 314 what = KERN_PROC_PID; 315 flag = pid; 316 } else if (kflag) { 317 what = KERN_PROC_KTHREAD; 318 flag = 0; 319 } else { 320 what = KERN_PROC_ALL; 321 flag = 0; 322 } 323 if (showthreads) 324 what |= KERN_PROC_SHOW_THREADS; 325 326 /* 327 * select procs 328 */ 329 kp = kvm_getprocs(kd, what, flag, sizeof(*kp), &nentries); 330 if (kp == NULL) 331 errx(1, "%s", kvm_geterr(kd)); 332 333 /* 334 * print header 335 */ 336 printheader(); 337 if (nentries == 0) 338 exit(1); 339 /* 340 * sort proc list, we convert from an array of structs to an array 341 * of pointers to make the sort cheaper. 342 */ 343 if ((kinfo = reallocarray(NULL, nentries, sizeof(*kinfo))) == NULL) 344 err(1, "failed to allocate memory for proc pointers"); 345 for (i = 0; i < nentries; i++) 346 kinfo[i] = &kp[i]; 347 qsort(kinfo, nentries, sizeof(*kinfo), pscomp); 348 /* 349 * for each proc, call each variable output function. 350 */ 351 for (i = lineno = 0; i < nentries; i++) { 352 if (showthreads == 0 && (kinfo[i]->p_flag & P_THREAD) != 0) 353 continue; 354 if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV || 355 (kinfo[i]->p_psflags & PS_CONTROLT ) == 0)) 356 continue; 357 if (showthreads && kinfo[i]->p_tid == -1) 358 continue; 359 for (vent = vhead; vent; vent = vent->next) { 360 (vent->var->oproc)(kinfo[i], vent); 361 if (vent->next != NULL) 362 (void)putchar(' '); 363 } 364 (void)putchar('\n'); 365 if (prtheader && lineno++ == prtheader - 4) { 366 (void)putchar('\n'); 367 printheader(); 368 lineno = 0; 369 } 370 } 371 exit(eval); 372 } 373 374 static void 375 scanvars(void) 376 { 377 struct varent *vent; 378 VAR *v; 379 int i; 380 381 for (vent = vhead; vent; vent = vent->next) { 382 v = vent->var; 383 i = strlen(v->header); 384 if (v->width < i) 385 v->width = i; 386 totwidth += v->width + 1; /* +1 for space */ 387 if (v->flag & COMM) 388 needcomm = 1; 389 if (v->flag & NLIST) 390 neednlist = 1; 391 } 392 totwidth--; 393 } 394 395 static int 396 pscomp(const void *v1, const void *v2) 397 { 398 const struct kinfo_proc *kp1 = *(const struct kinfo_proc **)v1; 399 const struct kinfo_proc *kp2 = *(const struct kinfo_proc **)v2; 400 int i; 401 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize) 402 403 if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0) 404 return (i); 405 if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0) 406 return (i); 407 if ((i = kp1->p_tdev - kp2->p_tdev) == 0 && 408 (i = kp1->p_ustart_sec - kp2->p_ustart_sec) == 0) 409 i = kp1->p_ustart_usec - kp2->p_ustart_usec; 410 return (i); 411 } 412 413 /* 414 * ICK (all for getopt), would rather hide the ugliness 415 * here than taint the main code. 416 * 417 * ps foo -> ps -foo 418 * ps 34 -> ps -p34 419 * 420 * The old convention that 't' with no trailing tty arg means the users 421 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 422 * feature is available with the option 'T', which takes no argument. 423 */ 424 static char * 425 kludge_oldps_options(char *s) 426 { 427 size_t len; 428 char *newopts, *ns, *cp; 429 430 len = strlen(s); 431 if ((newopts = ns = malloc(2 + len + 1)) == NULL) 432 err(1, NULL); 433 /* 434 * options begin with '-' 435 */ 436 if (*s != '-') 437 *ns++ = '-'; /* add option flag */ 438 439 /* 440 * gaze to end of argv[1] 441 */ 442 cp = s + len - 1; 443 /* 444 * if last letter is a 't' flag with no argument (in the context 445 * of the oldps options -- option string NOT starting with a '-' -- 446 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 447 */ 448 if (*cp == 't' && *s != '-') 449 *cp = 'T'; 450 else { 451 /* 452 * otherwise check for trailing number, which *may* be a 453 * pid. 454 */ 455 while (cp >= s && isdigit((unsigned char)*cp)) 456 --cp; 457 } 458 cp++; 459 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 460 ns += cp - s; 461 /* 462 * if there's a trailing number, and not a preceding 'p' (pid) or 463 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 464 */ 465 if (isdigit((unsigned char)*cp) && 466 (cp == s || (cp[-1] != 't' && cp[-1] != 'p' && 467 (cp - 1 == s || cp[-2] != 't')))) 468 *ns++ = 'p'; 469 /* and append the number */ 470 (void)strlcpy(ns, cp, newopts + len + 3 - ns); 471 472 return (newopts); 473 } 474 475 static void 476 usage(void) 477 { 478 (void)fprintf(stderr, 479 "usage: %s [-AaCceHhjkLlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n", 480 __progname); 481 (void)fprintf(stderr, 482 "%-*s[-t tty] [-U username] [-W swap]\n", (int)strlen(__progname) + 8, ""); 483 exit(1); 484 } 485