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