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