1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 /* 40 * ps -- print things about processes. 41 */ 42 43 #define _SYSCALL32 44 45 #include <stdio.h> 46 #include <ctype.h> 47 #include <string.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <pwd.h> 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 #include <sys/mkdev.h> 54 #include <unistd.h> 55 #include <stdlib.h> 56 #include <limits.h> 57 #include <dirent.h> 58 #include <procfs.h> 59 #include <sys/param.h> 60 #include <sys/ttold.h> 61 #include <libelf.h> 62 #include <gelf.h> 63 #include <locale.h> 64 #include <wctype.h> 65 #include <stdarg.h> 66 #include <sys/proc.h> 67 #include <priv_utils.h> 68 69 #define NTTYS 2 /* max ttys that can be specified with the -t option */ 70 /* only one tty can be specified with SunOS ps */ 71 #define SIZ 30 /* max processes that can be specified with -p and -g */ 72 #define ARGSIZ 30 /* size of buffer holding args for -t, -p, -u options */ 73 74 #define FSTYPE_MAX 8 75 76 struct psent { 77 psinfo_t *psinfo; 78 char *psargs; 79 int found; 80 }; 81 82 static int tplen, maxlen, twidth; 83 static char hdr[81]; 84 static struct winsize win; 85 86 static int retcode = 1; 87 static int lflg; /* long format */ 88 static int uflg; /* user-oriented output */ 89 static int aflg; /* Display all processes */ 90 static int eflg; /* Display environment as well as arguments */ 91 static int gflg; /* Display process group leaders */ 92 static int tflg; /* Processes running on specific terminals */ 93 static int rflg; /* Running processes only flag */ 94 static int Sflg; /* Accumulated time plus all reaped children */ 95 static int xflg; /* Include processes with no controlling tty */ 96 static int cflg; /* Display command name */ 97 static int vflg; /* Virtual memory-oriented output */ 98 static int nflg; /* Numerical output */ 99 static int pflg; /* Specific process id passed as argument */ 100 static int Uflg; /* Update private database, ups_data */ 101 static int errflg; 102 103 static char *gettty(); 104 static char argbuf[ARGSIZ]; 105 static char *parg; 106 static char *p1; /* points to successive option arguments */ 107 static uid_t my_uid; 108 static char stdbuf[BUFSIZ]; 109 110 static int ndev; /* number of devices */ 111 static int maxdev; /* number of devl structures allocated */ 112 113 #define DNINCR 100 114 #define DNSIZE 14 115 static struct devl { /* device list */ 116 char dname[DNSIZE]; /* device name */ 117 dev_t ddev; /* device number */ 118 } *devl; 119 120 static struct tty { 121 char *tname; 122 dev_t tdev; 123 } tty[NTTYS]; /* for t option */ 124 static int ntty = 0; 125 static pid_t pidsave; 126 static int pidwidth; 127 128 static char *procdir = "/proc"; /* standard /proc directory */ 129 static void usage(); /* print usage message and quit */ 130 static void getarg(void); 131 static void prtime(timestruc_t st); 132 static void przom(psinfo_t *psinfo); 133 static int num(char *); 134 static int preadargs(int, psinfo_t *, char *); 135 static int preadenvs(int, psinfo_t *, char *); 136 static int prcom(int, psinfo_t *, char *); 137 static int namencnt(char *, int, int); 138 static int pscompare(const void *, const void *); 139 static char *err_string(int); 140 141 extern int scrwidth(wchar_t); /* header file? */ 142 143 int 144 ucbmain(int argc, char **argv) 145 { 146 psinfo_t info; /* process information structure from /proc */ 147 char *psargs = NULL; /* pointer to buffer for -w and -ww options */ 148 char *svpsargs = NULL; 149 struct psent *psent; 150 int entsize; 151 int nent; 152 pid_t maxpid; 153 154 struct tty *ttyp = tty; 155 char *tmp; 156 char *p; 157 int c; 158 pid_t pid; /* pid: process id */ 159 pid_t ppid; /* ppid: parent process id */ 160 int i, found; 161 162 size_t size; 163 164 DIR *dirp; 165 struct dirent *dentp; 166 char psname[100]; 167 char asname[100]; 168 int pdlen; 169 size_t len; 170 171 (void) setlocale(LC_ALL, ""); 172 173 my_uid = getuid(); 174 175 /* 176 * This program needs the proc_owner privilege 177 */ 178 (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, 179 (char *)NULL); 180 181 /* 182 * calculate width of pid fields based on configured MAXPID 183 * (must be at least 5 to retain output format compatibility) 184 */ 185 maxpid = (pid_t)sysconf(_SC_MAXPID); 186 pidwidth = 1; 187 while ((maxpid /= 10) > 0) 188 ++pidwidth; 189 pidwidth = pidwidth < 5 ? 5 : pidwidth; 190 191 if (ioctl(1, TIOCGWINSZ, &win) == -1) 192 twidth = 80; 193 else 194 twidth = (win.ws_col == 0 ? 80 : win.ws_col); 195 196 /* add the '-' for BSD compatibility */ 197 if (argc > 1) { 198 if (argv[1][0] != '-' && !isdigit(argv[1][0])) { 199 len = strlen(argv[1]) + 2; 200 tmp = malloc(len); 201 if (tmp != NULL) { 202 (void) snprintf(tmp, len, "%s%s", "-", argv[1]); 203 argv[1] = tmp; 204 } 205 } 206 } 207 208 setbuf(stdout, stdbuf); 209 while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF) 210 switch (c) { 211 case 'g': 212 gflg++; /* include process group leaders */ 213 break; 214 case 'c': /* display internal command name */ 215 cflg++; 216 break; 217 case 'r': /* restrict output to running processes */ 218 rflg++; 219 break; 220 case 'S': /* display time by process and all reaped children */ 221 Sflg++; 222 break; 223 case 'x': /* process w/o controlling tty */ 224 xflg++; 225 break; 226 case 'l': /* long listing */ 227 lflg++; 228 uflg = vflg = 0; 229 break; 230 case 'u': /* user-oriented output */ 231 uflg++; 232 lflg = vflg = 0; 233 break; 234 case 'U': /* update private database ups_data */ 235 Uflg++; 236 break; 237 case 'w': /* increase display width */ 238 if (twidth < 132) 239 twidth = 132; 240 else /* second w option */ 241 twidth = NCARGS; 242 break; 243 case 'v': /* display virtual memory format */ 244 vflg++; 245 lflg = uflg = 0; 246 break; 247 case 'a': 248 /* 249 * display all processes except process group 250 * leaders and processes w/o controlling tty 251 */ 252 aflg++; 253 gflg++; 254 break; 255 case 'e': 256 /* Display environment along with aguments. */ 257 eflg++; 258 break; 259 case 'n': /* Display numerical output */ 260 nflg++; 261 break; 262 case 't': /* restrict output to named terminal */ 263 #define TSZ 30 264 tflg++; 265 gflg++; 266 xflg = 0; 267 268 p1 = optarg; 269 do { /* only loop through once (NTTYS = 2) */ 270 parg = argbuf; 271 if (ntty >= NTTYS-1) 272 break; 273 getarg(); 274 if ((p = malloc(TSZ+1)) == NULL) { 275 (void) fprintf(stderr, 276 "ps: no memory\n"); 277 exit(1); 278 } 279 p[0] = '\0'; 280 size = TSZ; 281 if (isdigit(*parg)) { 282 (void) strcpy(p, "tty"); 283 size -= 3; 284 } 285 286 (void) strncat(p, parg, size); 287 ttyp->tdev = PRNODEV; 288 if (parg && *parg == '?') 289 xflg++; 290 else { 291 char nambuf[TSZ+6]; /* for /dev/+\0 */ 292 struct stat64 s; 293 (void) strcpy(nambuf, "/dev/"); 294 (void) strcat(nambuf, p); 295 if (stat64(nambuf, &s) == 0) 296 ttyp->tdev = s.st_rdev; 297 } 298 ttyp++->tname = p; 299 ntty++; 300 } while (*p1); 301 break; 302 default: /* error on ? */ 303 errflg++; 304 break; 305 } 306 307 if (errflg) 308 usage(); 309 310 if (optind + 1 < argc) { /* more than one additional argument */ 311 (void) fprintf(stderr, "ps: too many arguments\n"); 312 usage(); 313 } 314 315 /* 316 * The -U option is obsolete. Attempts to use it cause ps to exit 317 * without printing anything. 318 */ 319 if (Uflg) 320 exit(0); 321 322 if (optind < argc) { /* user specified a specific proc id */ 323 pflg++; 324 p1 = argv[optind]; 325 parg = argbuf; 326 getarg(); 327 if (!num(parg)) { 328 (void) fprintf(stderr, 329 "ps: %s is an invalid non-numeric argument for a process id\n", parg); 330 usage(); 331 } 332 pidsave = (pid_t)atol(parg); 333 aflg = rflg = xflg = 0; 334 gflg++; 335 } 336 337 if (tflg) 338 ttyp->tname = NULL; 339 340 /* allocate an initial guess for the number of processes */ 341 entsize = 1024; 342 psent = malloc(entsize * sizeof (struct psent)); 343 if (psent == NULL) { 344 (void) fprintf(stderr, "ps: no memory\n"); 345 exit(1); 346 } 347 nent = 0; /* no active entries yet */ 348 349 if (lflg) { 350 (void) sprintf(hdr, 351 " F UID%*s%*s %%C PRI NI SZ RSS " 352 "WCHAN S TT TIME COMMAND", pidwidth + 1, "PID", 353 pidwidth + 1, "PPID"); 354 } else if (uflg) { 355 if (nflg) 356 (void) sprintf(hdr, 357 " UID%*s %%CPU %%MEM SZ RSS " 358 "TT S START TIME COMMAND", 359 pidwidth + 1, "PID"); 360 else 361 (void) sprintf(hdr, 362 "USER %*s %%CPU %%MEM SZ RSS " 363 "TT S START TIME COMMAND", 364 pidwidth + 1, "PID"); 365 } else if (vflg) { 366 (void) sprintf(hdr, 367 "%*s TT S TIME SIZE RSS %%CPU %%MEM " 368 "COMMAND", pidwidth + 1, "PID"); 369 } else 370 (void) sprintf(hdr, "%*s TT S TIME COMMAND", 371 pidwidth + 1, "PID"); 372 373 twidth = twidth - strlen(hdr) + 6; 374 (void) printf("%s\n", hdr); 375 376 if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) { 377 (void) fprintf(stderr, "ps: no memory\n"); 378 exit(1); 379 } 380 svpsargs = psargs; 381 382 /* 383 * Determine which processes to print info about by searching 384 * the /proc directory and looking at each process. 385 */ 386 if ((dirp = opendir(procdir)) == NULL) { 387 (void) fprintf(stderr, "ps: cannot open PROC directory %s\n", 388 procdir); 389 exit(1); 390 } 391 392 (void) strcpy(psname, procdir); 393 pdlen = strlen(psname); 394 psname[pdlen++] = '/'; 395 396 /* for each active process --- */ 397 while (dentp = readdir(dirp)) { 398 int psfd; /* file descriptor for /proc/nnnnn/psinfo */ 399 int asfd; /* file descriptor for /proc/nnnnn/as */ 400 401 if (dentp->d_name[0] == '.') /* skip . and .. */ 402 continue; 403 (void) strcpy(psname + pdlen, dentp->d_name); 404 (void) strcpy(asname, psname); 405 (void) strcat(psname, "/psinfo"); 406 (void) strcat(asname, "/as"); 407 retry: 408 if ((psfd = open(psname, O_RDONLY)) == -1) 409 continue; 410 asfd = -1; 411 if (psargs != NULL || eflg) { 412 413 /* now we need the proc_owner privilege */ 414 (void) __priv_bracket(PRIV_ON); 415 416 asfd = open(asname, O_RDONLY); 417 418 /* drop proc_owner privilege after open */ 419 (void) __priv_bracket(PRIV_OFF); 420 } 421 422 /* 423 * Get the info structure for the process 424 */ 425 if (read(psfd, &info, sizeof (info)) != sizeof (info)) { 426 int saverr = errno; 427 428 (void) close(psfd); 429 if (asfd > 0) 430 (void) close(asfd); 431 if (saverr == EAGAIN) 432 goto retry; 433 if (saverr != ENOENT) 434 (void) fprintf(stderr, "ps: read() on %s: %s\n", 435 psname, err_string(saverr)); 436 continue; 437 } 438 (void) close(psfd); 439 440 found = 0; 441 if (info.pr_lwp.pr_state == 0) /* can't happen? */ 442 goto closeit; 443 pid = info.pr_pid; 444 ppid = info.pr_ppid; 445 446 /* Display only process from command line */ 447 if (pflg) { /* pid in arg list */ 448 if (pidsave == pid) 449 found++; 450 else 451 goto closeit; 452 } 453 454 /* 455 * Omit "uninteresting" processes unless 'g' option. 456 */ 457 if ((ppid == 1) && !(gflg)) 458 goto closeit; 459 460 /* 461 * Omit non-running processes for 'r' option 462 */ 463 if (rflg && 464 !(info.pr_lwp.pr_sname == 'O' || 465 info.pr_lwp.pr_sname == 'R')) 466 goto closeit; 467 468 if (!found && !tflg && !aflg && info.pr_euid != my_uid) 469 goto closeit; 470 471 /* 472 * Read the args for the -w and -ww cases 473 */ 474 if (asfd > 0) { 475 if ((psargs != NULL && 476 preadargs(asfd, &info, psargs) == -1) || 477 (eflg && preadenvs(asfd, &info, psargs) == -1)) { 478 int saverr = errno; 479 480 (void) close(asfd); 481 if (saverr == EAGAIN) 482 goto retry; 483 if (saverr != ENOENT) 484 (void) fprintf(stderr, 485 "ps: read() on %s: %s\n", 486 asname, err_string(saverr)); 487 continue; 488 } 489 } else { 490 psargs = info.pr_psargs; 491 } 492 493 if (nent >= entsize) { 494 entsize *= 2; 495 psent = (struct psent *)realloc((char *)psent, 496 entsize * sizeof (struct psent)); 497 if (psent == NULL) { 498 (void) fprintf(stderr, "ps: no memory\n"); 499 exit(1); 500 } 501 } 502 if ((psent[nent].psinfo = malloc(sizeof (psinfo_t))) 503 == NULL) { 504 (void) fprintf(stderr, "ps: no memory\n"); 505 exit(1); 506 } 507 *psent[nent].psinfo = info; 508 if (psargs == NULL) 509 psent[nent].psargs = NULL; 510 else { 511 if ((psent[nent].psargs = malloc(strlen(psargs)+1)) 512 == NULL) { 513 (void) fprintf(stderr, "ps: no memory\n"); 514 exit(1); 515 } 516 (void) strcpy(psent[nent].psargs, psargs); 517 } 518 psent[nent].found = found; 519 nent++; 520 closeit: 521 if (asfd > 0) 522 (void) close(asfd); 523 psargs = svpsargs; 524 } 525 526 /* revert to non-privileged user */ 527 (void) __priv_relinquish(); 528 529 (void) closedir(dirp); 530 531 qsort((char *)psent, nent, sizeof (psent[0]), pscompare); 532 533 for (i = 0; i < nent; i++) { 534 struct psent *pp = &psent[i]; 535 if (prcom(pp->found, pp->psinfo, pp->psargs)) { 536 (void) printf("\n"); 537 retcode = 0; 538 } 539 } 540 541 return (retcode); 542 } 543 544 static void 545 usage() /* print usage message and quit */ 546 { 547 static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]"; 548 549 (void) fprintf(stderr, "usage: %s\n", usage1); 550 exit(1); 551 } 552 553 /* 554 * Read the process arguments from the process. 555 * This allows >PRARGSZ characters of arguments to be displayed but, 556 * unlike pr_psargs[], the process may have changed them. 557 */ 558 #define NARG 100 559 static int 560 preadargs(int pfd, psinfo_t *psinfo, char *psargs) 561 { 562 off_t argvoff = (off_t)psinfo->pr_argv; 563 size_t len; 564 char *psa = psargs; 565 int bsize = twidth; 566 int narg = NARG; 567 off_t argv[NARG]; 568 off_t argoff; 569 off_t nextargoff; 570 int i; 571 #ifdef _LP64 572 caddr32_t argv32[NARG]; 573 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); 574 #endif 575 576 if (psinfo->pr_nlwp == 0 || 577 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) 578 goto out; 579 580 (void) memset(psa, 0, bsize--); 581 nextargoff = 0; 582 errno = EIO; 583 while (bsize > 0) { 584 if (narg == NARG) { 585 (void) memset(argv, 0, sizeof (argv)); 586 #ifdef _LP64 587 if (is32) { 588 if ((i = pread(pfd, argv32, sizeof (argv32), 589 argvoff)) <= 0) { 590 if (i == 0 || errno == EIO) 591 break; 592 return (-1); 593 } 594 for (i = 0; i < NARG; i++) 595 argv[i] = argv32[i]; 596 } else 597 #endif 598 if ((i = pread(pfd, argv, sizeof (argv), 599 argvoff)) <= 0) { 600 if (i == 0 || errno == EIO) 601 break; 602 return (-1); 603 } 604 narg = 0; 605 } 606 if ((argoff = argv[narg++]) == 0) 607 break; 608 if (argoff != nextargoff && 609 (i = pread(pfd, psa, bsize, argoff)) <= 0) { 610 if (i == 0 || errno == EIO) 611 break; 612 return (-1); 613 } 614 len = strlen(psa); 615 psa += len; 616 *psa++ = ' '; 617 bsize -= len + 1; 618 nextargoff = argoff + len + 1; 619 #ifdef _LP64 620 argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); 621 #else 622 argvoff += sizeof (caddr_t); 623 #endif 624 } 625 while (psa > psargs && isspace(*(psa-1))) 626 psa--; 627 628 out: 629 *psa = '\0'; 630 if (strlen(psinfo->pr_psargs) > strlen(psargs)) 631 (void) strcpy(psargs, psinfo->pr_psargs); 632 633 return (0); 634 } 635 636 /* 637 * Read environment variables from the process. 638 * Append them to psargs if there is room. 639 */ 640 static int 641 preadenvs(int pfd, psinfo_t *psinfo, char *psargs) 642 { 643 off_t envpoff = (off_t)psinfo->pr_envp; 644 int len; 645 char *psa; 646 char *psainit; 647 int bsize; 648 int nenv = NARG; 649 off_t envp[NARG]; 650 off_t envoff; 651 off_t nextenvoff; 652 int i; 653 #ifdef _LP64 654 caddr32_t envp32[NARG]; 655 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); 656 #endif 657 658 psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs; 659 len = strlen(psa); 660 psa += len; 661 bsize = twidth - len - 1; 662 663 if (bsize <= 0 || psinfo->pr_nlwp == 0 || 664 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) 665 return (0); 666 667 nextenvoff = 0; 668 errno = EIO; 669 while (bsize > 0) { 670 if (nenv == NARG) { 671 (void) memset(envp, 0, sizeof (envp)); 672 #ifdef _LP64 673 if (is32) { 674 if ((i = pread(pfd, envp32, sizeof (envp32), 675 envpoff)) <= 0) { 676 if (i == 0 || errno == EIO) 677 break; 678 return (-1); 679 } 680 for (i = 0; i < NARG; i++) 681 envp[i] = envp32[i]; 682 } else 683 #endif 684 if ((i = pread(pfd, envp, sizeof (envp), 685 envpoff)) <= 0) { 686 if (i == 0 || errno == EIO) 687 break; 688 return (-1); 689 } 690 nenv = 0; 691 } 692 if ((envoff = envp[nenv++]) == 0) 693 break; 694 if (envoff != nextenvoff && 695 (i = pread(pfd, psa+1, bsize, envoff)) <= 0) { 696 if (i == 0 || errno == EIO) 697 break; 698 return (-1); 699 } 700 *psa++ = ' '; 701 len = strlen(psa); 702 psa += len; 703 bsize -= len + 1; 704 nextenvoff = envoff + len + 1; 705 #ifdef _LP64 706 envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); 707 #else 708 envpoff += sizeof (caddr_t); 709 #endif 710 } 711 while (psa > psainit && isspace(*(psa-1))) 712 psa--; 713 *psa = '\0'; 714 715 return (0); 716 } 717 718 /* 719 * getarg() finds the next argument in list and copies arg into argbuf. 720 * p1 first pts to arg passed back from getopt routine. p1 is then 721 * bumped to next character that is not a comma or blank -- p1 NULL 722 * indicates end of list. 723 */ 724 725 static void 726 getarg() 727 { 728 char *parga; 729 int c; 730 731 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 732 p1++; 733 734 parga = argbuf; 735 while ((c = *p1) != '\0' && c != ',' && !isspace(c)) { 736 if (parga < argbuf + ARGSIZ - 1) 737 *parga++ = c; 738 p1++; 739 } 740 *parga = '\0'; 741 742 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 743 p1++; 744 } 745 746 static char * 747 devlookup(dev_t ddev) 748 { 749 struct devl *dp; 750 int i; 751 752 for (dp = devl, i = 0; i < ndev; dp++, i++) { 753 if (dp->ddev == ddev) 754 return (dp->dname); 755 } 756 return (NULL); 757 } 758 759 static char * 760 devadd(char *name, dev_t ddev) 761 { 762 struct devl *dp; 763 int leng, start, i; 764 765 if (ndev == maxdev) { 766 maxdev += DNINCR; 767 devl = realloc(devl, maxdev * sizeof (struct devl)); 768 if (devl == NULL) { 769 (void) fprintf(stderr, 770 "ps: not enough memory for %d devices\n", maxdev); 771 exit(1); 772 } 773 } 774 dp = &devl[ndev++]; 775 776 dp->ddev = ddev; 777 if (name == NULL) { 778 (void) strcpy(dp->dname, "??"); 779 return (dp->dname); 780 } 781 782 leng = strlen(name); 783 /* Strip off /dev/ */ 784 if (leng < DNSIZE + 4) 785 (void) strcpy(dp->dname, &name[5]); 786 else { 787 start = leng - (DNSIZE - 1); 788 789 for (i = start; i < leng && name[i] != '/'; i++) 790 ; 791 if (i == leng) 792 (void) strlcpy(dp->dname, &name[start], DNSIZE); 793 else 794 (void) strlcpy(dp->dname, &name[i+1], DNSIZE); 795 } 796 return (dp->dname); 797 } 798 799 /* 800 * gettty returns the user's tty number or ? if none. 801 */ 802 static char * 803 gettty(psinfo_t *psinfo) 804 { 805 extern char *_ttyname_dev(dev_t, char *, size_t); 806 char devname[TTYNAME_MAX]; 807 char *retval; 808 809 if (psinfo->pr_ttydev == PRNODEV) 810 return ("?"); 811 812 if ((retval = devlookup(psinfo->pr_ttydev)) != NULL) 813 return (retval); 814 815 retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname)); 816 817 return (devadd(retval, psinfo->pr_ttydev)); 818 } 819 820 /* 821 * Print percent from 16-bit binary fraction [0 .. 1] 822 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). 823 */ 824 static void 825 prtpct(ushort_t pct) 826 { 827 uint_t value = pct; /* need 32 bits to compute with */ 828 829 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ 830 (void) printf("%3u.%u", value / 10, value % 10); 831 } 832 833 /* 834 * Print info about the process. 835 */ 836 static int 837 prcom(int found, psinfo_t *psinfo, char *psargs) 838 { 839 char *cp; 840 char *tp; 841 char *psa; 842 long tm; 843 int i, wcnt, length; 844 wchar_t wchar; 845 struct tty *ttyp; 846 847 /* 848 * If process is zombie, call print routine and return. 849 */ 850 if (psinfo->pr_nlwp == 0) { 851 if (tflg && !found) 852 return (0); 853 else { 854 przom(psinfo); 855 return (1); 856 } 857 } 858 859 /* 860 * Get current terminal. If none ("?") and 'a' is set, don't print 861 * info. If 't' is set, check if term is in list of desired terminals 862 * and print it if it is. 863 */ 864 i = 0; 865 tp = gettty(psinfo); 866 867 if (*tp == '?' && !found && !xflg) 868 return (0); 869 870 if (!(*tp == '?' && aflg) && tflg && !found) { 871 int match = 0; 872 char *other = NULL; 873 for (ttyp = tty; ttyp->tname != NULL; ttyp++) { 874 /* 875 * Look for a name match 876 */ 877 if (strcmp(tp, ttyp->tname) == 0) { 878 match = 1; 879 break; 880 } 881 /* 882 * Look for same device under different names. 883 */ 884 if ((other == NULL) && 885 (psinfo->pr_ttydev == ttyp->tdev)) 886 other = ttyp->tname; 887 } 888 if (!match) { 889 if (other == NULL) 890 return (0); 891 tp = other; 892 } 893 } 894 895 if (lflg) 896 (void) printf("%2x", psinfo->pr_flag & 0377); 897 if (uflg) { 898 if (!nflg) { 899 struct passwd *pwd; 900 901 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 902 /* USER */ 903 (void) printf("%-8.8s", pwd->pw_name); 904 else 905 /* UID */ 906 (void) printf(" %7.7d", (int)psinfo->pr_euid); 907 } else { 908 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 909 } 910 } else if (lflg) 911 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 912 913 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */ 914 if (lflg) 915 (void) printf("%*d", pidwidth + 1, 916 (int)psinfo->pr_ppid); /* PPID */ 917 if (lflg) 918 (void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */ 919 if (uflg) { 920 prtpct(psinfo->pr_pctcpu); /* %CPU */ 921 prtpct(psinfo->pr_pctmem); /* %MEM */ 922 } 923 if (lflg) { 924 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */ 925 (void) printf("%3d", psinfo->pr_lwp.pr_nice); /* NICE */ 926 } 927 if (lflg || uflg) { 928 if (psinfo->pr_flag & SSYS) /* SZ */ 929 (void) printf(" 0"); 930 else if (psinfo->pr_size) 931 (void) printf("%5lu", (ulong_t)psinfo->pr_size); 932 else 933 (void) printf(" ?"); 934 if (psinfo->pr_flag & SSYS) /* RSS */ 935 (void) printf(" 0"); 936 else if (psinfo->pr_rssize) 937 (void) printf("%5lu", (ulong_t)psinfo->pr_rssize); 938 else 939 (void) printf(" ?"); 940 } 941 if (lflg) { /* WCHAN */ 942 if (psinfo->pr_lwp.pr_sname != 'S') { 943 (void) printf(" "); 944 } else if (psinfo->pr_lwp.pr_wchan) { 945 (void) printf(" %+8.8lx", 946 (ulong_t)psinfo->pr_lwp.pr_wchan); 947 } else { 948 (void) printf(" ?"); 949 } 950 } 951 if ((tplen = strlen(tp)) > 9) 952 maxlen = twidth - tplen + 9; 953 else 954 maxlen = twidth; 955 956 if (!lflg) 957 (void) printf(" %-8.14s", tp); /* TTY */ 958 (void) printf(" %c", psinfo->pr_lwp.pr_sname); /* STATE */ 959 if (lflg) 960 (void) printf(" %-8.14s", tp); /* TTY */ 961 if (uflg) 962 prtime(psinfo->pr_start); /* START */ 963 964 /* time just for process */ 965 tm = psinfo->pr_time.tv_sec; 966 if (Sflg) { /* calculate time for process and all reaped children */ 967 tm += psinfo->pr_ctime.tv_sec; 968 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec 969 >= 1000000000) 970 tm += 1; 971 } 972 973 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */ 974 975 if (vflg) { 976 if (psinfo->pr_flag & SSYS) /* SZ */ 977 (void) printf(" 0"); 978 else if (psinfo->pr_size) 979 (void) printf("%5lu", (ulong_t)psinfo->pr_size); 980 else 981 (void) printf(" ?"); 982 if (psinfo->pr_flag & SSYS) /* SZ */ 983 (void) printf(" 0"); 984 else if (psinfo->pr_rssize) 985 (void) printf("%5lu", (ulong_t)psinfo->pr_rssize); 986 else 987 (void) printf(" ?"); 988 prtpct(psinfo->pr_pctcpu); /* %CPU */ 989 prtpct(psinfo->pr_pctmem); /* %MEM */ 990 } 991 if (cflg) { /* CMD */ 992 wcnt = namencnt(psinfo->pr_fname, 16, maxlen); 993 (void) printf(" %.*s", wcnt, psinfo->pr_fname); 994 return (1); 995 } 996 /* 997 * PRARGSZ == length of cmd arg string. 998 */ 999 if (psargs == NULL) { 1000 psa = &psinfo->pr_psargs[0]; 1001 i = PRARGSZ; 1002 tp = &psinfo->pr_psargs[PRARGSZ]; 1003 } else { 1004 psa = psargs; 1005 i = strlen(psargs); 1006 tp = psa + i; 1007 } 1008 1009 for (cp = psa; cp < tp; /* empty */) { 1010 if (*cp == 0) 1011 break; 1012 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1013 if (length < 0 || !iswprint(wchar)) { 1014 (void) printf(" [ %.16s ]", psinfo->pr_fname); 1015 return (1); 1016 } 1017 cp += length; 1018 } 1019 wcnt = namencnt(psa, i, maxlen); 1020 #if 0 1021 /* dumps core on really long strings */ 1022 (void) printf(" %.*s", wcnt, psa); 1023 #else 1024 (void) putchar(' '); 1025 (void) fwrite(psa, 1, wcnt, stdout); 1026 #endif 1027 return (1); 1028 } 1029 1030 /* 1031 * Print starting time of process unless process started more than 24 hours 1032 * ago, in which case the date is printed. 1033 */ 1034 static void 1035 prtime(timestruc_t st) 1036 { 1037 char sttim[26]; 1038 static time_t tim = 0L; 1039 time_t starttime; 1040 1041 if (tim == 0L) 1042 tim = time((time_t *)0); 1043 starttime = st.tv_sec; 1044 if (tim - starttime > 24*60*60) { 1045 (void) strftime(sttim, sizeof (sttim), "%b %d", 1046 localtime(&starttime)); 1047 } else { 1048 (void) strftime(sttim, sizeof (sttim), "%H:%M:%S", 1049 localtime(&starttime)); 1050 } 1051 (void) printf("%9.9s", sttim); 1052 } 1053 1054 static void 1055 przom(psinfo_t *psinfo) 1056 { 1057 long tm; 1058 1059 if (lflg) 1060 (void) printf("%2x", psinfo->pr_flag & 0377); 1061 if (uflg) { 1062 struct passwd *pwd; 1063 1064 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 1065 (void) printf("%-8.8s", pwd->pw_name); /* USER */ 1066 else 1067 (void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */ 1068 } else if (lflg) 1069 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 1070 1071 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */ 1072 if (lflg) 1073 (void) printf("%*d", pidwidth + 1, 1074 (int)psinfo->pr_ppid); /* PPID */ 1075 if (lflg) 1076 (void) printf(" 0"); /* CP */ 1077 if (uflg) { 1078 prtpct(0); /* %CPU */ 1079 prtpct(0); /* %MEM */ 1080 } 1081 if (lflg) { 1082 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */ 1083 (void) printf(" "); /* NICE */ 1084 } 1085 if (lflg || uflg) { 1086 (void) printf(" 0"); /* SZ */ 1087 (void) printf(" 0"); /* RSS */ 1088 } 1089 if (lflg) 1090 (void) printf(" "); /* WCHAN */ 1091 (void) printf(" "); /* TTY */ 1092 (void) printf("%c", psinfo->pr_lwp.pr_sname); /* STATE */ 1093 if (uflg) 1094 (void) printf(" "); /* START */ 1095 1096 /* time just for process */ 1097 tm = psinfo->pr_time.tv_sec; 1098 if (Sflg) { /* calculate time for process and all reaped children */ 1099 tm += psinfo->pr_ctime.tv_sec; 1100 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec 1101 >= 1000000000) 1102 tm += 1; 1103 } 1104 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */ 1105 1106 if (vflg) { 1107 (void) printf(" 0"); /* SZ */ 1108 (void) printf(" 0"); /* RSS */ 1109 prtpct(0); /* %CPU */ 1110 prtpct(0); /* %MEM */ 1111 } 1112 (void) printf(" %.*s", maxlen, " <defunct>"); 1113 } 1114 1115 /* 1116 * Returns true iff string is all numeric. 1117 */ 1118 static int 1119 num(char *s) 1120 { 1121 int c; 1122 1123 if (s == NULL) 1124 return (0); 1125 c = *s; 1126 do { 1127 if (!isdigit(c)) 1128 return (0); 1129 } while ((c = *++s) != '\0'); 1130 return (1); 1131 } 1132 1133 /* 1134 * Function to compute the number of printable bytes in a multibyte 1135 * command string ("internationalization"). 1136 */ 1137 static int 1138 namencnt(char *cmd, int eucsize, int scrsize) 1139 { 1140 int eucwcnt = 0, scrwcnt = 0; 1141 int neucsz, nscrsz; 1142 wchar_t wchar; 1143 1144 while (*cmd != '\0') { 1145 if ((neucsz = mbtowc(&wchar, cmd, MB_LEN_MAX)) < 0) 1146 return (8); /* default to use for illegal chars */ 1147 if ((nscrsz = scrwidth(wchar)) == 0) 1148 return (8); 1149 if (eucwcnt + neucsz > eucsize || scrwcnt + nscrsz > scrsize) 1150 break; 1151 eucwcnt += neucsz; 1152 scrwcnt += nscrsz; 1153 cmd += neucsz; 1154 } 1155 return (eucwcnt); 1156 } 1157 1158 static int 1159 pscompare(const void *v1, const void *v2) 1160 { 1161 const struct psent *p1 = v1; 1162 const struct psent *p2 = v2; 1163 int i; 1164 1165 if (uflg) 1166 i = p2->psinfo->pr_pctcpu - p1->psinfo->pr_pctcpu; 1167 else if (vflg) 1168 i = p2->psinfo->pr_rssize - p1->psinfo->pr_rssize; 1169 else 1170 i = p1->psinfo->pr_ttydev - p2->psinfo->pr_ttydev; 1171 if (i == 0) 1172 i = p1->psinfo->pr_pid - p2->psinfo->pr_pid; 1173 return (i); 1174 } 1175 1176 static char * 1177 err_string(int err) 1178 { 1179 static char buf[32]; 1180 char *str = strerror(err); 1181 1182 if (str == NULL) 1183 (void) sprintf(str = buf, "Errno #%d", err); 1184 1185 return (str); 1186 } 1187