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 /* 23 * Copyright 2006 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 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * ps -- print things about processes. 34 */ 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <string.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <pwd.h> 41 #include <grp.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/mkdev.h> 45 #include <unistd.h> 46 #include <stdlib.h> 47 #include <limits.h> 48 #include <dirent.h> 49 #include <sys/signal.h> 50 #include <sys/fault.h> 51 #include <sys/syscall.h> 52 #include <sys/time.h> 53 #include <procfs.h> 54 #include <locale.h> 55 #include <wctype.h> 56 #include <wchar.h> 57 #include <libw.h> 58 #include <stdarg.h> 59 #include <sys/proc.h> 60 #include <sys/pset.h> 61 #include <project.h> 62 #include <zone.h> 63 64 #define min(a, b) ((a) > (b) ? (b) : (a)) 65 #define max(a, b) ((a) < (b) ? (b) : (a)) 66 67 #define NTTYS 20 /* initial size of table for -t option */ 68 #define SIZ 30 /* initial size of tables for -p, -s, -g, and -z */ 69 70 /* 71 * Size of buffer holding args for t, p, s, g, u, U, G, z options. 72 * Set to ZONENAME_MAX, the minimum value needed to allow any 73 * zone to be specified. 74 */ 75 #define ARGSIZ ZONENAME_MAX 76 77 #define MAXUGNAME 10 /* max chars in a user/group name or printed u/g id */ 78 79 /* Structure for storing user or group info */ 80 struct ugdata { 81 id_t id; /* numeric user-id or group-id */ 82 char name[MAXUGNAME+1]; /* user/group name, null terminated */ 83 }; 84 85 struct ughead { 86 size_t size; /* number of ugdata structs allocated */ 87 size_t nent; /* number of active entries */ 88 struct ugdata *ent; /* pointer to array of actual entries */ 89 }; 90 91 enum fname { /* enumeration of field names */ 92 F_USER, /* effective user of the process */ 93 F_RUSER, /* real user of the process */ 94 F_GROUP, /* effective group of the process */ 95 F_RGROUP, /* real group of the process */ 96 F_UID, /* numeric effective uid of the process */ 97 F_RUID, /* numeric real uid of the process */ 98 F_GID, /* numeric effective gid of the process */ 99 F_RGID, /* numeric real gid of the process */ 100 F_PID, /* process id */ 101 F_PPID, /* parent process id */ 102 F_PGID, /* process group id */ 103 F_SID, /* session id */ 104 F_PSR, /* bound processor */ 105 F_LWP, /* lwp-id */ 106 F_NLWP, /* number of lwps */ 107 F_OPRI, /* old priority (obsolete) */ 108 F_PRI, /* new priority */ 109 F_F, /* process flags */ 110 F_S, /* letter indicating the state */ 111 F_C, /* processor utilization (obsolete) */ 112 F_PCPU, /* percent of recently used cpu time */ 113 F_PMEM, /* percent of physical memory used (rss) */ 114 F_OSZ, /* virtual size of the process in pages */ 115 F_VSZ, /* virtual size of the process in kilobytes */ 116 F_RSS, /* resident set size of the process in kilobytes */ 117 F_NICE, /* "nice" value of the process */ 118 F_CLASS, /* scheduler class */ 119 F_STIME, /* start time of the process, hh:mm:ss or Month Day */ 120 F_ETIME, /* elapsed time of the process, [[dd-]hh:]mm:ss */ 121 F_TIME, /* cpu time of the process, [[dd-]hh:]mm:ss */ 122 F_TTY, /* name of the controlling terminal */ 123 F_ADDR, /* address of the process (obsolete) */ 124 F_WCHAN, /* wait channel (sleep condition variable) */ 125 F_FNAME, /* file name of command */ 126 F_COMM, /* name of command (argv[0] value) */ 127 F_ARGS, /* name of command plus all its arguments */ 128 F_TASKID, /* task id */ 129 F_PROJID, /* project id */ 130 F_PROJECT, /* project name of the process */ 131 F_PSET, /* bound processor set */ 132 F_ZONE, /* zone name */ 133 F_ZONEID, /* zone id */ 134 F_CTID /* process contract id */ 135 }; 136 137 struct field { 138 struct field *next; /* linked list */ 139 int fname; /* field index */ 140 const char *header; /* header to use */ 141 int width; /* width of field */ 142 }; 143 144 static struct field *fields = NULL; /* fields selected via -o */ 145 static struct field *last_field = NULL; 146 static int do_header = 0; 147 static struct timeval now; 148 149 /* array of defined fields, in fname order */ 150 struct def_field { 151 const char *fname; 152 const char *header; 153 int width; 154 int minwidth; 155 }; 156 157 static struct def_field fname[] = { 158 /* fname header width minwidth */ 159 { "user", "USER", 8, 8 }, 160 { "ruser", "RUSER", 8, 8 }, 161 { "group", "GROUP", 8, 8 }, 162 { "rgroup", "RGROUP", 8, 8 }, 163 { "uid", "UID", 5, 5 }, 164 { "ruid", "RUID", 5, 5 }, 165 { "gid", "GID", 5, 5 }, 166 { "rgid", "RGID", 5, 5 }, 167 { "pid", "PID", 5, 5 }, 168 { "ppid", "PPID", 5, 5 }, 169 { "pgid", "PGID", 5, 5 }, 170 { "sid", "SID", 5, 5 }, 171 { "psr", "PSR", 3, 2 }, 172 { "lwp", "LWP", 6, 2 }, 173 { "nlwp", "NLWP", 4, 2 }, 174 { "opri", "PRI", 3, 2 }, 175 { "pri", "PRI", 3, 2 }, 176 { "f", "F", 2, 2 }, 177 { "s", "S", 1, 1 }, 178 { "c", "C", 2, 2 }, 179 { "pcpu", "%CPU", 4, 4 }, 180 { "pmem", "%MEM", 4, 4 }, 181 { "osz", "SZ", 4, 4 }, 182 { "vsz", "VSZ", 4, 4 }, 183 { "rss", "RSS", 4, 4 }, 184 { "nice", "NI", 2, 2 }, 185 { "class", "CLS", 4, 2 }, 186 { "stime", "STIME", 8, 8 }, 187 { "etime", "ELAPSED", 11, 7 }, 188 { "time", "TIME", 11, 5 }, 189 { "tty", "TT", 7, 7 }, 190 #ifdef _LP64 191 { "addr", "ADDR", 16, 8 }, 192 { "wchan", "WCHAN", 16, 8 }, 193 #else 194 { "addr", "ADDR", 8, 8 }, 195 { "wchan", "WCHAN", 8, 8 }, 196 #endif 197 { "fname", "COMMAND", 8, 8 }, 198 { "comm", "COMMAND", 80, 8 }, 199 { "args", "COMMAND", 80, 80 }, 200 { "taskid", "TASKID", 5, 5 }, 201 { "projid", "PROJID", 5, 5 }, 202 { "project", "PROJECT", 8, 8 }, 203 { "pset", "PSET", 3, 3 }, 204 { "zone", "ZONE", 8, 8 }, 205 { "zoneid", "ZONEID", 5, 5 }, 206 { "ctid", "CTID", 5, 5 }, 207 }; 208 209 #define NFIELDS (sizeof (fname) / sizeof (fname[0])) 210 211 static int retcode = 1; 212 static int lflg; 213 static int Aflg; 214 static int uflg; 215 static int Uflg; 216 static int Gflg; 217 static int aflg; 218 static int dflg; 219 static int Lflg; 220 static int Pflg; 221 static int yflg; 222 static int pflg; 223 static int fflg; 224 static int cflg; 225 static int jflg; 226 static int gflg; 227 static int sflg; 228 static int tflg; 229 static int zflg; 230 static int Zflg; 231 static uid_t tuid = -1; 232 static int errflg; 233 234 static int ndev; /* number of devices */ 235 static int maxdev; /* number of devl structures allocated */ 236 237 #define DNINCR 100 238 #define DNSIZE 14 239 static struct devl { /* device list */ 240 char dname[DNSIZE]; /* device name */ 241 dev_t ddev; /* device number */ 242 } *devl; 243 244 static struct tty { 245 char *tname; 246 dev_t tdev; 247 } *tty = NULL; /* for t option */ 248 static size_t ttysz = 0; 249 static int ntty = 0; 250 251 static pid_t *pid = NULL; /* for p option */ 252 static size_t pidsz = 0; 253 static size_t npid = 0; 254 255 static pid_t *grpid = NULL; /* for g option */ 256 static size_t grpidsz = 0; 257 static int ngrpid = 0; 258 259 static pid_t *sessid = NULL; /* for s option */ 260 static size_t sessidsz = 0; 261 static int nsessid = 0; 262 263 static zoneid_t *zoneid = NULL; /* for z option */ 264 static size_t zoneidsz = 0; 265 static int nzoneid = 0; 266 267 static int kbytes_per_page; 268 static int pidwidth; 269 270 static char *procdir = "/proc"; /* standard /proc directory */ 271 272 static struct ughead euid_tbl; /* table to store selected euid's */ 273 static struct ughead ruid_tbl; /* table to store selected real uid's */ 274 static struct ughead egid_tbl; /* table to store selected egid's */ 275 static struct ughead rgid_tbl; /* table to store selected real gid's */ 276 static prheader_t *lpsinfobuf; /* buffer to contain lpsinfo */ 277 static size_t lpbufsize; 278 279 /* 280 * This constant defines the sentinal number of process IDs below which we 281 * only examine individual entries in /proc rather than scanning through 282 * /proc. This optimization is a huge win in the common case. 283 */ 284 #define PTHRESHOLD 40 285 286 static void usage(void); 287 static char *getarg(char **); 288 static char *parse_format(char *); 289 static char *gettty(psinfo_t *); 290 static int prfind(int, psinfo_t *, char **); 291 static void prcom(psinfo_t *, char *); 292 static void prtpct(ushort_t, int); 293 static void print_time(time_t, int); 294 static void print_field(psinfo_t *, struct field *, const char *); 295 static void print_zombie_field(psinfo_t *, struct field *, const char *); 296 static void pr_fields(psinfo_t *, const char *, 297 void (*print_fld)(psinfo_t *, struct field *, const char *)); 298 static int search(pid_t *, int, pid_t); 299 static void add_ugentry(struct ughead *, char *); 300 static int uconv(struct ughead *); 301 static int gconv(struct ughead *); 302 static int ugfind(uid_t, struct ughead *); 303 static void prtime(timestruc_t, int, int); 304 static void przom(psinfo_t *); 305 static int namencnt(char *, int, int); 306 static char *err_string(int); 307 static int print_proc(char *pname); 308 static time_t delta_secs(const timestruc_t *); 309 static int str2id(const char *, pid_t *, long, long); 310 static void *Realloc(void *, size_t); 311 static int pidcmp(const void *p1, const void *p2); 312 313 int 314 main(int argc, char **argv) 315 { 316 char *p; 317 char *p1; 318 char *parg; 319 int c; 320 int i; 321 int pgerrflg = 0; /* err flg: non-numeric arg w/p & g options */ 322 size_t size; 323 DIR *dirp; 324 struct dirent *dentp; 325 pid_t maxpid; 326 pid_t id; 327 int ret; 328 329 (void) setlocale(LC_ALL, ""); 330 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 331 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 332 #endif 333 (void) textdomain(TEXT_DOMAIN); 334 335 (void) memset(&euid_tbl, 0, sizeof (euid_tbl)); 336 (void) memset(&ruid_tbl, 0, sizeof (ruid_tbl)); 337 (void) memset(&egid_tbl, 0, sizeof (egid_tbl)); 338 (void) memset(&rgid_tbl, 0, sizeof (rgid_tbl)); 339 340 kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024; 341 342 (void) gettimeofday(&now, NULL); 343 344 /* 345 * calculate width of pid fields based on configured MAXPID 346 * (must be at least 5 to retain output format compatibility) 347 */ 348 id = maxpid = (pid_t)sysconf(_SC_MAXPID); 349 pidwidth = 1; 350 while ((id /= 10) > 0) 351 ++pidwidth; 352 pidwidth = pidwidth < 5 ? 5 : pidwidth; 353 354 fname[F_PID].width = fname[F_PPID].width = pidwidth; 355 fname[F_PGID].width = fname[F_SID].width = pidwidth; 356 357 while ((c = getopt(argc, argv, "jlfceAadLPyZt:p:g:u:U:G:n:s:o:z:")) != 358 EOF) 359 switch (c) { 360 case 'l': /* long listing */ 361 lflg++; 362 break; 363 case 'f': /* full listing */ 364 fflg++; 365 break; 366 case 'j': 367 jflg++; 368 break; 369 case 'c': 370 /* 371 * Format output to reflect scheduler changes: 372 * high numbers for high priorities and don't 373 * print nice or p_cpu values. 'c' option only 374 * effective when used with 'l' or 'f' options. 375 */ 376 cflg++; 377 break; 378 case 'A': /* list every process */ 379 case 'e': /* (obsolete) list every process */ 380 Aflg++; 381 tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0; 382 zflg = 0; 383 break; 384 case 'a': 385 /* 386 * Same as 'e' except no session group leaders 387 * and no non-terminal processes. 388 */ 389 aflg++; 390 break; 391 case 'd': /* same as e except no session leaders */ 392 dflg++; 393 break; 394 case 'L': /* show lwps */ 395 Lflg++; 396 break; 397 case 'P': /* show bound processor */ 398 Pflg++; 399 break; 400 case 'y': /* omit F & ADDR, report RSS & SZ in Kby */ 401 yflg++; 402 break; 403 case 'n': /* no longer needed; retain as no-op */ 404 (void) fprintf(stderr, 405 gettext("ps: warning: -n option ignored\n")); 406 break; 407 case 't': /* terminals */ 408 #define TSZ 30 409 tflg++; 410 p1 = optarg; 411 do { 412 char nambuf[TSZ+6]; /* for "/dev/" + '\0' */ 413 struct stat64 s; 414 parg = getarg(&p1); 415 p = Realloc(NULL, TSZ+1); /* for '\0' */ 416 /* zero the buffer before using it */ 417 p[0] = '\0'; 418 size = TSZ; 419 if (isdigit(*parg)) { 420 (void) strcpy(p, "tty"); 421 size -= 3; 422 } 423 (void) strncat(p, parg, size); 424 if (ntty == ttysz) { 425 if ((ttysz *= 2) == 0) 426 ttysz = NTTYS; 427 tty = Realloc(tty, 428 (ttysz + 1) * sizeof (struct tty)); 429 } 430 tty[ntty].tdev = PRNODEV; 431 (void) strcpy(nambuf, "/dev/"); 432 (void) strcat(nambuf, p); 433 if (stat64(nambuf, &s) == 0) 434 tty[ntty].tdev = s.st_rdev; 435 tty[ntty++].tname = p; 436 } while (*p1); 437 break; 438 case 'p': /* proc ids */ 439 pflg++; 440 p1 = optarg; 441 do { 442 pid_t id; 443 444 parg = getarg(&p1); 445 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 446 pgerrflg++; 447 (void) fprintf(stderr, 448 gettext("ps: %s "), parg); 449 if (ret == EINVAL) 450 (void) fprintf(stderr, 451 gettext("is an invalid " 452 "non-numeric argument")); 453 else 454 (void) fprintf(stderr, 455 gettext("exceeds valid " 456 "range")); 457 (void) fprintf(stderr, 458 gettext(" for -p option\n")); 459 continue; 460 } 461 462 if (npid == pidsz) { 463 if ((pidsz *= 2) == 0) 464 pidsz = SIZ; 465 pid = Realloc(pid, 466 pidsz * sizeof (pid_t)); 467 } 468 pid[npid++] = id; 469 } while (*p1); 470 break; 471 case 's': /* session */ 472 sflg++; 473 p1 = optarg; 474 do { 475 pid_t id; 476 477 parg = getarg(&p1); 478 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 479 pgerrflg++; 480 (void) fprintf(stderr, 481 gettext("ps: %s "), parg); 482 if (ret == EINVAL) 483 (void) fprintf(stderr, 484 gettext("is an invalid " 485 "non-numeric argument")); 486 else 487 (void) fprintf(stderr, 488 gettext("exceeds valid " 489 "range")); 490 (void) fprintf(stderr, 491 gettext(" for -s option\n")); 492 continue; 493 } 494 495 if (nsessid == sessidsz) { 496 if ((sessidsz *= 2) == 0) 497 sessidsz = SIZ; 498 sessid = Realloc(sessid, 499 sessidsz * sizeof (pid_t)); 500 } 501 sessid[nsessid++] = id; 502 } while (*p1); 503 break; 504 case 'g': /* proc group */ 505 gflg++; 506 p1 = optarg; 507 do { 508 pid_t id; 509 510 parg = getarg(&p1); 511 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 512 pgerrflg++; 513 (void) fprintf(stderr, 514 gettext("ps: %s "), parg); 515 if (ret == EINVAL) 516 (void) fprintf(stderr, 517 gettext("is an invalid " 518 "non-numeric argument")); 519 else 520 (void) fprintf(stderr, 521 gettext("exceeds valid " 522 "range")); 523 (void) fprintf(stderr, 524 gettext(" for -g option\n")); 525 continue; 526 } 527 528 if (ngrpid == grpidsz) { 529 if ((grpidsz *= 2) == 0) 530 grpidsz = SIZ; 531 grpid = Realloc(grpid, 532 grpidsz * sizeof (pid_t)); 533 } 534 grpid[ngrpid++] = id; 535 } while (*p1); 536 break; 537 case 'u': /* effective user name or number */ 538 uflg++; 539 p1 = optarg; 540 do { 541 parg = getarg(&p1); 542 add_ugentry(&euid_tbl, parg); 543 } while (*p1); 544 break; 545 case 'U': /* real user name or number */ 546 Uflg++; 547 p1 = optarg; 548 do { 549 parg = getarg(&p1); 550 add_ugentry(&ruid_tbl, parg); 551 } while (*p1); 552 break; 553 case 'G': /* real group name or number */ 554 Gflg++; 555 p1 = optarg; 556 do { 557 parg = getarg(&p1); 558 add_ugentry(&rgid_tbl, parg); 559 } while (*p1); 560 break; 561 case 'o': /* output format */ 562 p = optarg; 563 while ((p = parse_format(p)) != NULL) 564 ; 565 break; 566 case 'z': /* zone name or number */ 567 zflg++; 568 p1 = optarg; 569 do { 570 zoneid_t id; 571 572 parg = getarg(&p1); 573 if (zone_get_id(parg, &id) != 0) { 574 pgerrflg++; 575 (void) fprintf(stderr, 576 gettext("ps: unknown zone %s\n"), 577 parg); 578 continue; 579 } 580 581 if (nzoneid == zoneidsz) { 582 if ((zoneidsz *= 2) == 0) 583 zoneidsz = SIZ; 584 zoneid = Realloc(zoneid, 585 zoneidsz * sizeof (zoneid_t)); 586 } 587 zoneid[nzoneid++] = id; 588 } while (*p1); 589 break; 590 case 'Z': /* show zone name */ 591 Zflg++; 592 break; 593 default: /* error on ? */ 594 errflg++; 595 break; 596 } 597 598 if (errflg || optind < argc || pgerrflg) 599 usage(); 600 601 if (tflg) 602 tty[ntty].tname = NULL; 603 /* 604 * If an appropriate option has not been specified, use the 605 * current terminal and effective uid as the default. 606 */ 607 if (!(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) { 608 psinfo_t info; 609 int procfd; 610 char *name; 611 char pname[100]; 612 613 /* get our own controlling tty name using /proc */ 614 (void) snprintf(pname, sizeof (pname), 615 "%s/self/psinfo", procdir); 616 if ((procfd = open(pname, O_RDONLY)) < 0 || 617 read(procfd, (char *)&info, sizeof (info)) < 0 || 618 info.pr_ttydev == PRNODEV) { 619 (void) fprintf(stderr, 620 gettext("ps: no controlling terminal\n")); 621 exit(1); 622 } 623 (void) close(procfd); 624 625 i = 0; 626 name = gettty(&info); 627 if (*name == '?') { 628 (void) fprintf(stderr, 629 gettext("ps: can't find controlling terminal\n")); 630 exit(1); 631 } 632 if (ntty == ttysz) { 633 if ((ttysz *= 2) == 0) 634 ttysz = NTTYS; 635 tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty)); 636 } 637 tty[ntty].tdev = info.pr_ttydev; 638 tty[ntty++].tname = name; 639 tty[ntty].tname = NULL; 640 tflg++; 641 tuid = getuid(); 642 } 643 if (Aflg) { 644 Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0; 645 zflg = 0; 646 } 647 if (Aflg | aflg | dflg) 648 tflg = 0; 649 650 i = 0; /* prepare to exit on name lookup errors */ 651 i += uconv(&euid_tbl); 652 i += uconv(&ruid_tbl); 653 i += gconv(&egid_tbl); 654 i += gconv(&rgid_tbl); 655 if (i) 656 exit(1); 657 658 /* allocate a buffer for lwpsinfo structures */ 659 lpbufsize = 4096; 660 if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) { 661 (void) fprintf(stderr, 662 gettext("ps: no memory\n")); 663 exit(1); 664 } 665 666 if (fields) { /* print user-specified header */ 667 if (do_header) { 668 struct field *f; 669 670 for (f = fields; f != NULL; f = f->next) { 671 if (f != fields) 672 (void) printf(" "); 673 switch (f->fname) { 674 case F_TTY: 675 (void) printf("%-*s", 676 f->width, f->header); 677 break; 678 case F_FNAME: 679 case F_COMM: 680 case F_ARGS: 681 /* 682 * Print these headers full width 683 * unless they appear at the end. 684 */ 685 if (f->next != NULL) { 686 (void) printf("%-*s", 687 f->width, f->header); 688 } else { 689 (void) printf("%s", 690 f->header); 691 } 692 break; 693 default: 694 (void) printf("%*s", 695 f->width, f->header); 696 break; 697 } 698 } 699 (void) printf("\n"); 700 } 701 } else { /* print standard header */ 702 if (lflg) { 703 if (yflg) 704 (void) printf(" S"); 705 else 706 (void) printf(" F S"); 707 } 708 if (Zflg) 709 (void) printf(" ZONE"); 710 if (fflg) { 711 if (lflg) 712 (void) printf(" "); 713 (void) printf(" UID"); 714 } else if (lflg) 715 (void) printf(" UID"); 716 717 (void) printf(" %*s", pidwidth, "PID"); 718 if (lflg || fflg) 719 (void) printf(" %*s", pidwidth, "PPID"); 720 if (jflg) 721 (void) printf(" %*s %*s", pidwidth, "PGID", 722 pidwidth, "SID"); 723 if (Lflg) 724 (void) printf(" LWP"); 725 if (Pflg) 726 (void) printf(" PSR"); 727 if (Lflg && fflg) 728 (void) printf(" NLWP"); 729 if (cflg) 730 (void) printf(" CLS PRI"); 731 else if (lflg || fflg) { 732 (void) printf(" C"); 733 if (lflg) 734 (void) printf(" PRI NI"); 735 } 736 if (lflg) { 737 if (yflg) 738 (void) printf(" RSS SZ WCHAN"); 739 else 740 (void) printf(" ADDR SZ WCHAN"); 741 } 742 if (fflg) 743 (void) printf(" STIME"); 744 if (Lflg) 745 (void) printf(" TTY LTIME CMD\n"); 746 else 747 (void) printf(" TTY TIME CMD\n"); 748 } 749 750 751 if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|tflg|gflg|sflg|zflg) && 752 npid <= PTHRESHOLD) { 753 /* 754 * If we are looking at specific processes go straight 755 * to their /proc entries and don't scan /proc. 756 */ 757 int i; 758 759 (void) qsort(pid, npid, sizeof (pid_t), pidcmp); 760 for (i = 0; i < npid; i++) { 761 char pname[12]; 762 763 if (i >= 1 && pid[i] == pid[i - 1]) 764 continue; 765 (void) sprintf(pname, "%d", (int)pid[i]); 766 if (print_proc(pname) == 0) 767 retcode = 0; 768 } 769 } else { 770 /* 771 * Determine which processes to print info about by searching 772 * the /proc directory and looking at each process. 773 */ 774 if ((dirp = opendir(procdir)) == NULL) { 775 (void) fprintf(stderr, 776 gettext("ps: cannot open PROC directory %s\n"), 777 procdir); 778 exit(1); 779 } 780 781 /* for each active process --- */ 782 while (dentp = readdir(dirp)) { 783 if (dentp->d_name[0] == '.') /* skip . and .. */ 784 continue; 785 if (print_proc(dentp->d_name) == 0) 786 retcode = 0; 787 } 788 789 (void) closedir(dirp); 790 } 791 return (retcode); 792 } 793 794 795 int 796 print_proc(char *pid_name) 797 { 798 char pname[PATH_MAX]; 799 int pdlen; 800 int found; 801 int procfd; /* filedescriptor for /proc/nnnnn/psinfo */ 802 char *tp; /* ptr to ttyname, if any */ 803 psinfo_t info; /* process information from /proc */ 804 lwpsinfo_t *lwpsinfo; /* array of lwpsinfo structs */ 805 806 pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name); 807 if (pdlen >= sizeof (pname) - 10) 808 return (1); 809 retry: 810 (void) strcpy(&pname[pdlen], "psinfo"); 811 if ((procfd = open(pname, O_RDONLY)) == -1) { 812 /* Process may have exited meanwhile. */ 813 return (1); 814 } 815 /* 816 * Get the info structure for the process and close quickly. 817 */ 818 if (read(procfd, (char *)&info, sizeof (info)) < 0) { 819 int saverr = errno; 820 821 (void) close(procfd); 822 if (saverr == EAGAIN) 823 goto retry; 824 if (saverr != ENOENT) 825 (void) fprintf(stderr, 826 gettext("ps: read() on %s: %s\n"), 827 pname, err_string(saverr)); 828 return (1); 829 } 830 (void) close(procfd); 831 832 found = 0; 833 if (info.pr_lwp.pr_state == 0) /* can't happen? */ 834 return (1); 835 836 /* 837 * Omit session group leaders for 'a' and 'd' options. 838 */ 839 if ((info.pr_pid == info.pr_sid) && (dflg || aflg)) 840 return (1); 841 if (Aflg || dflg) 842 found++; 843 else if (pflg && search(pid, npid, info.pr_pid)) 844 found++; /* ppid in p option arg list */ 845 else if (uflg && ugfind(info.pr_euid, &euid_tbl)) 846 found++; /* puid in u option arg list */ 847 else if (Uflg && ugfind(info.pr_uid, &ruid_tbl)) 848 found++; /* puid in U option arg list */ 849 #ifdef NOT_YET 850 else if (gflg && ugfind(info.pr_egid, &egid_tbl)) 851 found++; /* pgid in g option arg list */ 852 #endif /* NOT_YET */ 853 else if (Gflg && ugfind(info.pr_gid, &rgid_tbl)) 854 found++; /* pgid in G option arg list */ 855 else if (gflg && search(grpid, ngrpid, info.pr_pgid)) 856 found++; /* grpid in g option arg list */ 857 else if (sflg && search(sessid, nsessid, info.pr_sid)) 858 found++; /* sessid in s option arg list */ 859 else if (zflg && search(zoneid, nzoneid, info.pr_zoneid)) 860 found++; /* zoneid in z option arg list */ 861 if (!found && !tflg && !aflg) 862 return (1); 863 if (!prfind(found, &info, &tp)) 864 return (1); 865 if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) { 866 ssize_t prsz; 867 868 (void) strcpy(&pname[pdlen], "lpsinfo"); 869 if ((procfd = open(pname, O_RDONLY)) == -1) 870 return (1); 871 /* 872 * Get the info structures for the lwps. 873 */ 874 prsz = read(procfd, lpsinfobuf, lpbufsize); 875 if (prsz == -1) { 876 int saverr = errno; 877 878 (void) close(procfd); 879 if (saverr == EAGAIN) 880 goto retry; 881 if (saverr != ENOENT) 882 (void) fprintf(stderr, 883 gettext("ps: read() on %s: %s\n"), 884 pname, err_string(saverr)); 885 return (1); 886 } 887 (void) close(procfd); 888 if (prsz == lpbufsize) { 889 /* 890 * buffer overflow. Realloc new buffer. 891 * Error handling is done in Realloc(). 892 */ 893 lpbufsize *= 2; 894 lpsinfobuf = Realloc(lpsinfobuf, lpbufsize); 895 goto retry; 896 } 897 if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb)) 898 goto retry; 899 lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1); 900 } 901 if (!Lflg || (info.pr_nlwp + info.pr_nzomb) <= 1) { 902 prcom(&info, tp); 903 } else { 904 int nlwp = 0; 905 906 do { 907 info.pr_lwp = *lwpsinfo; 908 prcom(&info, tp); 909 /* LINTED improper alignment */ 910 lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo + 911 lpsinfobuf->pr_entsize); 912 } while (++nlwp < lpsinfobuf->pr_nent); 913 } 914 return (0); 915 } 916 917 918 static void 919 usage(void) /* print usage message and quit */ 920 { 921 static char usage1[] = 922 "ps [ -aAdeflcjLPyZ ] [ -o format ] [ -t termlist ]"; 923 static char usage2[] = 924 "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]"; 925 static char usage3[] = 926 "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ] [ -z zonelist ]"; 927 static char usage4[] = 928 " 'format' is one or more of:"; 929 static char usage5[] = 930 "\tuser ruser group rgroup uid ruid gid rgid pid ppid pgid " 931 "sid taskid ctid"; 932 static char usage6[] = 933 "\tpri opri pcpu pmem vsz rss osz nice class time etime stime zone " 934 "zoneid"; 935 static char usage7[] = 936 "\tf s c lwp nlwp psr tty addr wchan fname comm args " 937 "projid project pset"; 938 939 (void) fprintf(stderr, 940 gettext("usage: %s\n%s\n%s\n%s\n%s\n%s\n%s\n"), 941 gettext(usage1), gettext(usage2), gettext(usage3), 942 gettext(usage4), gettext(usage5), gettext(usage6), gettext(usage7)); 943 exit(1); 944 } 945 946 /* 947 * getarg() finds the next argument in list and copies arg into argbuf. 948 * p1 first pts to arg passed back from getopt routine. p1 is then 949 * bumped to next character that is not a comma or blank -- p1 NULL 950 * indicates end of list. 951 */ 952 static char * 953 getarg(char **pp1) 954 { 955 static char argbuf[ARGSIZ]; 956 char *p1 = *pp1; 957 char *parga = argbuf; 958 int c; 959 960 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 961 p1++; 962 963 while ((c = *p1) != '\0' && c != ',' && !isspace(c)) { 964 if (parga < argbuf + ARGSIZ - 1) 965 *parga++ = c; 966 p1++; 967 } 968 *parga = '\0'; 969 970 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 971 p1++; 972 973 *pp1 = p1; 974 975 return (argbuf); 976 } 977 978 /* 979 * parse_format() takes the argument to the -o option, 980 * sets up the next output field structure, and returns 981 * a pointer to any further output field specifier(s). 982 * As a side-effect, it increments errflg if encounters a format error. 983 */ 984 static char * 985 parse_format(char *arg) 986 { 987 int c; 988 char *name; 989 char *header = NULL; 990 int width = 0; 991 struct def_field *df; 992 struct field *f; 993 994 while ((c = *arg) != '\0' && (c == ',' || isspace(c))) 995 arg++; 996 if (c == '\0') 997 return (NULL); 998 name = arg; 999 arg = strpbrk(arg, " \t\r\v\f\n,="); 1000 if (arg != NULL) { 1001 c = *arg; 1002 *arg++ = '\0'; 1003 if (c == '=') { 1004 char *s; 1005 1006 header = arg; 1007 arg = NULL; 1008 width = strlen(header); 1009 s = header + width; 1010 while (s > header && isspace(*--s)) 1011 *s = '\0'; 1012 while (isspace(*header)) 1013 header++; 1014 } 1015 } 1016 for (df = &fname[0]; df < &fname[NFIELDS]; df++) 1017 if (strcmp(name, df->fname) == 0) { 1018 if (strcmp(name, "lwp") == 0) 1019 Lflg++; 1020 break; 1021 } 1022 if (df >= &fname[NFIELDS]) { 1023 (void) fprintf(stderr, 1024 gettext("ps: unknown output format: -o %s\n"), 1025 name); 1026 errflg++; 1027 return (arg); 1028 } 1029 if ((f = malloc(sizeof (*f))) == NULL) { 1030 (void) fprintf(stderr, 1031 gettext("ps: malloc() for output format failed, %s\n"), 1032 err_string(errno)); 1033 exit(1); 1034 } 1035 f->next = NULL; 1036 f->fname = df - &fname[0]; 1037 f->header = header? header : df->header; 1038 if (width == 0) 1039 width = df->width; 1040 if (*f->header != '\0') 1041 do_header = 1; 1042 f->width = max(width, df->minwidth); 1043 1044 if (fields == NULL) 1045 fields = last_field = f; 1046 else { 1047 last_field->next = f; 1048 last_field = f; 1049 } 1050 1051 return (arg); 1052 } 1053 1054 static char * 1055 devlookup(dev_t ddev) 1056 { 1057 struct devl *dp; 1058 int i; 1059 1060 for (dp = devl, i = 0; i < ndev; dp++, i++) { 1061 if (dp->ddev == ddev) 1062 return (dp->dname); 1063 } 1064 return (NULL); 1065 } 1066 1067 static char * 1068 devadd(char *name, dev_t ddev) 1069 { 1070 struct devl *dp; 1071 int leng, start, i; 1072 1073 if (ndev == maxdev) { 1074 maxdev += DNINCR; 1075 devl = Realloc(devl, maxdev * sizeof (struct devl)); 1076 } 1077 dp = &devl[ndev++]; 1078 1079 dp->ddev = ddev; 1080 if (name == NULL) { 1081 (void) strcpy(dp->dname, "??"); 1082 return (dp->dname); 1083 } 1084 1085 leng = strlen(name); 1086 /* Strip off /dev/ */ 1087 if (leng < DNSIZE + 4) 1088 (void) strcpy(dp->dname, &name[5]); 1089 else { 1090 start = leng - DNSIZE - 1; 1091 1092 for (i = start; i < leng && name[i] != '/'; i++) 1093 ; 1094 if (i == leng) 1095 (void) strncpy(dp->dname, &name[start], DNSIZE); 1096 else 1097 (void) strncpy(dp->dname, &name[i+1], DNSIZE); 1098 } 1099 return (dp->dname); 1100 } 1101 1102 /* 1103 * gettty returns the user's tty number or ? if none. 1104 */ 1105 static char * 1106 gettty(psinfo_t *psinfo) 1107 { 1108 extern char *_ttyname_dev(dev_t, char *, size_t); 1109 char devname[TTYNAME_MAX]; 1110 char *retval; 1111 1112 if (psinfo->pr_ttydev == PRNODEV) 1113 return ("?"); 1114 1115 if ((retval = devlookup(psinfo->pr_ttydev)) != NULL) 1116 return (retval); 1117 1118 retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname)); 1119 1120 return (devadd(retval, psinfo->pr_ttydev)); 1121 } 1122 1123 /* 1124 * Find the process's tty and return 1 if process is to be printed. 1125 */ 1126 static int 1127 prfind(int found, psinfo_t *psinfo, char **tpp) 1128 { 1129 char *tp; 1130 struct tty *ttyp; 1131 1132 if (psinfo->pr_nlwp == 0) { 1133 /* process is a zombie */ 1134 *tpp = "?"; 1135 if (tflg && !found) 1136 return (0); 1137 return (1); 1138 } 1139 1140 /* 1141 * Get current terminal. If none ("?") and 'a' is set, don't print 1142 * info. If 't' is set, check if term is in list of desired terminals 1143 * and print it if it is. 1144 */ 1145 tp = gettty(psinfo); 1146 if (aflg && *tp == '?') { 1147 *tpp = tp; 1148 return (0); 1149 } 1150 if (tflg && !found) { 1151 int match = 0; 1152 char *other = NULL; 1153 for (ttyp = tty; ttyp->tname != NULL; ttyp++) { 1154 /* 1155 * Look for a name match 1156 */ 1157 if (strcmp(tp, ttyp->tname) == 0) { 1158 match = 1; 1159 break; 1160 } 1161 /* 1162 * Look for same device under different names. 1163 */ 1164 if ((other == NULL) && 1165 (ttyp->tdev != PRNODEV) && 1166 (psinfo->pr_ttydev == ttyp->tdev)) 1167 other = ttyp->tname; 1168 } 1169 if (!match && (other != NULL)) { 1170 /* 1171 * found under a different name 1172 */ 1173 match = 1; 1174 tp = other; 1175 } 1176 if (!match || (tuid != -1 && tuid != psinfo->pr_euid)) { 1177 /* 1178 * not found OR not matching euid 1179 */ 1180 *tpp = tp; 1181 return (0); 1182 } 1183 } 1184 *tpp = tp; 1185 return (1); 1186 } 1187 1188 /* 1189 * Print info about the process. 1190 */ 1191 static void 1192 prcom(psinfo_t *psinfo, char *ttyp) 1193 { 1194 char *cp; 1195 long tm; 1196 int bytesleft; 1197 int wcnt, length; 1198 wchar_t wchar; 1199 struct passwd *pwd; 1200 int zombie_lwp; 1201 char zonename[ZONENAME_MAX]; 1202 1203 /* 1204 * If process is zombie, call zombie print routine and return. 1205 */ 1206 if (psinfo->pr_nlwp == 0) { 1207 if (fields != NULL) 1208 pr_fields(psinfo, ttyp, print_zombie_field); 1209 else 1210 przom(psinfo); 1211 return; 1212 } 1213 1214 zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); 1215 1216 /* 1217 * If user specified '-o format', print requested fields and return. 1218 */ 1219 if (fields != NULL) { 1220 pr_fields(psinfo, ttyp, print_field); 1221 return; 1222 } 1223 1224 /* 1225 * All fields before 'PID' are printed with a trailing space as a 1226 * spearator, rather than keeping track of which column is first. All 1227 * other fields are printed with a leading space. 1228 */ 1229 if (lflg) { 1230 if (!yflg) 1231 (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ 1232 (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ 1233 } 1234 1235 if (Zflg) { /* ZONE */ 1236 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 1237 sizeof (zonename)) < 0) { 1238 (void) printf("%7.7d ", ((int)psinfo->pr_zoneid)); 1239 } else { 1240 (void) printf("%8.8s ", zonename); 1241 } 1242 } 1243 1244 if (fflg) { /* UID */ 1245 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 1246 (void) printf("%8.8s ", pwd->pw_name); 1247 else 1248 (void) printf("%7.7d ", (int)psinfo->pr_euid); 1249 } else if (lflg) { 1250 (void) printf("%6d ", (int)psinfo->pr_euid); 1251 } 1252 (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ 1253 if (lflg || fflg) 1254 (void) printf(" %*d", pidwidth, 1255 (int)psinfo->pr_ppid); /* PPID */ 1256 if (jflg) { 1257 (void) printf(" %*d", pidwidth, 1258 (int)psinfo->pr_pgid); /* PGID */ 1259 (void) printf(" %*d", pidwidth, 1260 (int)psinfo->pr_sid); /* SID */ 1261 } 1262 if (Lflg) 1263 (void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */ 1264 if (Pflg) { 1265 if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE) /* PSR */ 1266 (void) printf(" -"); 1267 else 1268 (void) printf(" %3d", psinfo->pr_lwp.pr_bindpro); 1269 } 1270 if (Lflg && fflg) /* NLWP */ 1271 (void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb); 1272 if (cflg) { 1273 if (zombie_lwp) /* CLS */ 1274 (void) printf(" "); 1275 else 1276 (void) printf(" %4s", psinfo->pr_lwp.pr_clname); 1277 (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ 1278 } else if (lflg || fflg) { 1279 (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ 1280 if (lflg) { /* PRI NI */ 1281 /* 1282 * Print priorities the old way (lower numbers 1283 * mean higher priority) and print nice value 1284 * for time sharing procs. 1285 */ 1286 (void) printf(" %3d", psinfo->pr_lwp.pr_oldpri); 1287 if (psinfo->pr_lwp.pr_oldpri != 0) 1288 (void) printf(" %2d", psinfo->pr_lwp.pr_nice); 1289 else 1290 (void) printf(" %2.2s", 1291 psinfo->pr_lwp.pr_clname); 1292 } 1293 } 1294 if (lflg) { 1295 if (yflg) { 1296 if (psinfo->pr_flag & SSYS) /* RSS */ 1297 (void) printf(" 0"); 1298 else if (psinfo->pr_rssize) 1299 (void) printf(" %5lu", 1300 (ulong_t)psinfo->pr_rssize); 1301 else 1302 (void) printf(" ?"); 1303 if (psinfo->pr_flag & SSYS) /* SZ */ 1304 (void) printf(" 0"); 1305 else if (psinfo->pr_size) 1306 (void) printf(" %6lu", 1307 (ulong_t)psinfo->pr_size); 1308 else 1309 (void) printf(" ?"); 1310 } else { 1311 #ifndef _LP64 1312 if (psinfo->pr_addr) /* ADDR */ 1313 (void) printf(" %8lx", 1314 (ulong_t)psinfo->pr_addr); 1315 else 1316 #endif 1317 (void) printf(" ?"); 1318 if (psinfo->pr_flag & SSYS) /* SZ */ 1319 (void) printf(" 0"); 1320 else if (psinfo->pr_size) 1321 (void) printf(" %6lu", 1322 (ulong_t)psinfo->pr_size / kbytes_per_page); 1323 else 1324 (void) printf(" ?"); 1325 } 1326 if (psinfo->pr_lwp.pr_sname != 'S') /* WCHAN */ 1327 (void) printf(" "); 1328 #ifndef _LP64 1329 else if (psinfo->pr_lwp.pr_wchan) 1330 (void) printf(" %8lx", 1331 (ulong_t)psinfo->pr_lwp.pr_wchan); 1332 #endif 1333 else 1334 (void) printf(" ?"); 1335 } 1336 if (fflg) { /* STIME */ 1337 if (Lflg) 1338 prtime(psinfo->pr_lwp.pr_start, 9, 1); 1339 else 1340 prtime(psinfo->pr_start, 9, 1); 1341 } 1342 (void) printf(" %-8.14s", ttyp); /* TTY */ 1343 if (Lflg) { 1344 tm = psinfo->pr_lwp.pr_time.tv_sec; 1345 if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) 1346 tm++; 1347 } else { 1348 tm = psinfo->pr_time.tv_sec; 1349 if (psinfo->pr_time.tv_nsec > 500000000) 1350 tm++; 1351 } 1352 (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* [L]TIME */ 1353 1354 if (zombie_lwp) { 1355 (void) printf(" <defunct>\n"); 1356 return; 1357 } 1358 1359 if (!fflg) { /* CMD */ 1360 wcnt = namencnt(psinfo->pr_fname, 16, 8); 1361 (void) printf(" %.*s\n", wcnt, psinfo->pr_fname); 1362 return; 1363 } 1364 1365 /* 1366 * PRARGSZ == length of cmd arg string. 1367 */ 1368 psinfo->pr_psargs[PRARGSZ-1] = '\0'; 1369 bytesleft = PRARGSZ; 1370 for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { 1371 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1372 if (length == 0) 1373 break; 1374 if (length < 0 || !iswprint(wchar)) { 1375 if (length < 0) 1376 length = 1; 1377 if (bytesleft <= length) { 1378 *cp = '\0'; 1379 break; 1380 } 1381 /* omit the unprintable character */ 1382 (void) memmove(cp, cp+length, bytesleft-length); 1383 length = 0; 1384 } 1385 bytesleft -= length; 1386 } 1387 wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ); 1388 (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs); 1389 } 1390 1391 /* 1392 * Print percent from 16-bit binary fraction [0 .. 1] 1393 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). 1394 */ 1395 static void 1396 prtpct(ushort_t pct, int width) 1397 { 1398 uint_t value = pct; /* need 32 bits to compute with */ 1399 1400 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ 1401 if (value >= 1000) 1402 value = 999; 1403 if ((width -= 2) < 2) 1404 width = 2; 1405 (void) printf("%*u.%u", width, value / 10, value % 10); 1406 } 1407 1408 static void 1409 print_time(time_t tim, int width) 1410 { 1411 char buf[30]; 1412 time_t seconds; 1413 time_t minutes; 1414 time_t hours; 1415 time_t days; 1416 1417 if (tim < 0) { 1418 (void) printf("%*s", width, "-"); 1419 return; 1420 } 1421 1422 seconds = tim % 60; 1423 tim /= 60; 1424 minutes = tim % 60; 1425 tim /= 60; 1426 hours = tim % 24; 1427 days = tim / 24; 1428 1429 if (days > 0) { 1430 (void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld", 1431 days, hours, minutes, seconds); 1432 } else if (hours > 0) { 1433 (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld", 1434 hours, minutes, seconds); 1435 } else { 1436 (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld", 1437 minutes, seconds); 1438 } 1439 1440 (void) printf("%*s", width, buf); 1441 } 1442 1443 static void 1444 print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) 1445 { 1446 int width = f->width; 1447 struct passwd *pwd; 1448 struct group *grp; 1449 time_t cputime; 1450 int bytesleft; 1451 int wcnt; 1452 wchar_t wchar; 1453 char *cp; 1454 int length; 1455 ulong_t mask; 1456 char c, *csave; 1457 int zombie_lwp; 1458 1459 zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); 1460 1461 switch (f->fname) { 1462 case F_RUSER: 1463 if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) 1464 (void) printf("%*s", width, pwd->pw_name); 1465 else 1466 (void) printf("%*d", width, (int)psinfo->pr_uid); 1467 break; 1468 case F_USER: 1469 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 1470 (void) printf("%*s", width, pwd->pw_name); 1471 else 1472 (void) printf("%*d", width, (int)psinfo->pr_euid); 1473 break; 1474 case F_RGROUP: 1475 if ((grp = getgrgid(psinfo->pr_gid)) != NULL) 1476 (void) printf("%*s", width, grp->gr_name); 1477 else 1478 (void) printf("%*d", width, (int)psinfo->pr_gid); 1479 break; 1480 case F_GROUP: 1481 if ((grp = getgrgid(psinfo->pr_egid)) != NULL) 1482 (void) printf("%*s", width, grp->gr_name); 1483 else 1484 (void) printf("%*d", width, (int)psinfo->pr_egid); 1485 break; 1486 case F_RUID: 1487 (void) printf("%*d", width, (int)psinfo->pr_uid); 1488 break; 1489 case F_UID: 1490 (void) printf("%*d", width, (int)psinfo->pr_euid); 1491 break; 1492 case F_RGID: 1493 (void) printf("%*d", width, (int)psinfo->pr_gid); 1494 break; 1495 case F_GID: 1496 (void) printf("%*d", width, (int)psinfo->pr_egid); 1497 break; 1498 case F_PID: 1499 (void) printf("%*d", width, (int)psinfo->pr_pid); 1500 break; 1501 case F_PPID: 1502 (void) printf("%*d", width, (int)psinfo->pr_ppid); 1503 break; 1504 case F_PGID: 1505 (void) printf("%*d", width, (int)psinfo->pr_pgid); 1506 break; 1507 case F_SID: 1508 (void) printf("%*d", width, (int)psinfo->pr_sid); 1509 break; 1510 case F_PSR: 1511 if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE) 1512 (void) printf("%*s", width, "-"); 1513 else 1514 (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro); 1515 break; 1516 case F_LWP: 1517 (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid); 1518 break; 1519 case F_NLWP: 1520 (void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb); 1521 break; 1522 case F_OPRI: 1523 if (zombie_lwp) 1524 (void) printf("%*s", width, "-"); 1525 else 1526 (void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri); 1527 break; 1528 case F_PRI: 1529 if (zombie_lwp) 1530 (void) printf("%*s", width, "-"); 1531 else 1532 (void) printf("%*d", width, psinfo->pr_lwp.pr_pri); 1533 break; 1534 case F_F: 1535 mask = 0xffffffffUL; 1536 if (width < 8) 1537 mask >>= (8 - width) * 4; 1538 (void) printf("%*lx", width, psinfo->pr_flag & mask); 1539 break; 1540 case F_S: 1541 (void) printf("%*c", width, psinfo->pr_lwp.pr_sname); 1542 break; 1543 case F_C: 1544 if (zombie_lwp) 1545 (void) printf("%*s", width, "-"); 1546 else 1547 (void) printf("%*d", width, psinfo->pr_lwp.pr_cpu); 1548 break; 1549 case F_PCPU: 1550 if (zombie_lwp) 1551 (void) printf("%*s", width, "-"); 1552 else if (Lflg) 1553 prtpct(psinfo->pr_lwp.pr_pctcpu, width); 1554 else 1555 prtpct(psinfo->pr_pctcpu, width); 1556 break; 1557 case F_PMEM: 1558 prtpct(psinfo->pr_pctmem, width); 1559 break; 1560 case F_OSZ: 1561 (void) printf("%*lu", width, 1562 (ulong_t)psinfo->pr_size / kbytes_per_page); 1563 break; 1564 case F_VSZ: 1565 (void) printf("%*lu", width, (ulong_t)psinfo->pr_size); 1566 break; 1567 case F_RSS: 1568 (void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize); 1569 break; 1570 case F_NICE: 1571 /* if pr_oldpri is zero, then this class has no nice */ 1572 if (zombie_lwp) 1573 (void) printf("%*s", width, "-"); 1574 else if (psinfo->pr_lwp.pr_oldpri != 0) 1575 (void) printf("%*d", width, psinfo->pr_lwp.pr_nice); 1576 else 1577 (void) printf("%*.*s", width, width, 1578 psinfo->pr_lwp.pr_clname); 1579 break; 1580 case F_CLASS: 1581 if (zombie_lwp) 1582 (void) printf("%*s", width, "-"); 1583 else 1584 (void) printf("%*.*s", width, width, 1585 psinfo->pr_lwp.pr_clname); 1586 break; 1587 case F_STIME: 1588 if (Lflg) 1589 prtime(psinfo->pr_lwp.pr_start, width, 0); 1590 else 1591 prtime(psinfo->pr_start, width, 0); 1592 break; 1593 case F_ETIME: 1594 if (Lflg) 1595 print_time(delta_secs(&psinfo->pr_lwp.pr_start), 1596 width); 1597 else 1598 print_time(delta_secs(&psinfo->pr_start), width); 1599 break; 1600 case F_TIME: 1601 if (Lflg) { 1602 cputime = psinfo->pr_lwp.pr_time.tv_sec; 1603 if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) 1604 cputime++; 1605 } else { 1606 cputime = psinfo->pr_time.tv_sec; 1607 if (psinfo->pr_time.tv_nsec > 500000000) 1608 cputime++; 1609 } 1610 print_time(cputime, width); 1611 break; 1612 case F_TTY: 1613 (void) printf("%-*s", width, ttyp); 1614 break; 1615 case F_ADDR: 1616 if (zombie_lwp) 1617 (void) printf("%*s", width, "-"); 1618 else if (Lflg) 1619 (void) printf("%*lx", width, 1620 (long)psinfo->pr_lwp.pr_addr); 1621 else 1622 (void) printf("%*lx", width, (long)psinfo->pr_addr); 1623 break; 1624 case F_WCHAN: 1625 if (!zombie_lwp && psinfo->pr_lwp.pr_wchan) 1626 (void) printf("%*lx", width, 1627 (long)psinfo->pr_lwp.pr_wchan); 1628 else 1629 (void) printf("%*.*s", width, width, "-"); 1630 break; 1631 case F_FNAME: 1632 /* 1633 * Print full width unless this is the last output format. 1634 */ 1635 if (zombie_lwp) { 1636 if (f->next != NULL) 1637 (void) printf("%-*s", width, "<defunct>"); 1638 else 1639 (void) printf("%s", "<defunct>"); 1640 break; 1641 } 1642 wcnt = namencnt(psinfo->pr_fname, 16, width); 1643 if (f->next != NULL) 1644 (void) printf("%-*.*s", width, wcnt, psinfo->pr_fname); 1645 else 1646 (void) printf("%-.*s", wcnt, psinfo->pr_fname); 1647 break; 1648 case F_COMM: 1649 if (zombie_lwp) { 1650 if (f->next != NULL) 1651 (void) printf("%-*s", width, "<defunct>"); 1652 else 1653 (void) printf("%s", "<defunct>"); 1654 break; 1655 } 1656 csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n"); 1657 if (csave) { 1658 c = *csave; 1659 *csave = '\0'; 1660 } 1661 /* FALLTHROUGH */ 1662 case F_ARGS: 1663 /* 1664 * PRARGSZ == length of cmd arg string. 1665 */ 1666 if (zombie_lwp) { 1667 (void) printf("%-*s", width, "<defunct>"); 1668 break; 1669 } 1670 psinfo->pr_psargs[PRARGSZ-1] = '\0'; 1671 bytesleft = PRARGSZ; 1672 for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { 1673 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1674 if (length == 0) 1675 break; 1676 if (length < 0 || !iswprint(wchar)) { 1677 if (length < 0) 1678 length = 1; 1679 if (bytesleft <= length) { 1680 *cp = '\0'; 1681 break; 1682 } 1683 /* omit the unprintable character */ 1684 (void) memmove(cp, cp+length, bytesleft-length); 1685 length = 0; 1686 } 1687 bytesleft -= length; 1688 } 1689 wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width); 1690 /* 1691 * Print full width unless this is the last format. 1692 */ 1693 if (f->next != NULL) 1694 (void) printf("%-*.*s", width, wcnt, 1695 psinfo->pr_psargs); 1696 else 1697 (void) printf("%-.*s", wcnt, 1698 psinfo->pr_psargs); 1699 if (f->fname == F_COMM && csave) 1700 *csave = c; 1701 break; 1702 case F_TASKID: 1703 (void) printf("%*d", width, (int)psinfo->pr_taskid); 1704 break; 1705 case F_PROJID: 1706 (void) printf("%*d", width, (int)psinfo->pr_projid); 1707 break; 1708 case F_PROJECT: 1709 { 1710 struct project cproj; 1711 char proj_buf[PROJECT_BUFSZ]; 1712 1713 if ((getprojbyid(psinfo->pr_projid, &cproj, 1714 (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) 1715 (void) printf("%*d", width, 1716 (int)psinfo->pr_projid); 1717 else 1718 (void) printf("%*s", width, 1719 (cproj.pj_name != NULL) ? 1720 cproj.pj_name : "---"); 1721 } 1722 break; 1723 case F_PSET: 1724 if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE) 1725 (void) printf("%*s", width, "-"); 1726 else 1727 (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset); 1728 break; 1729 case F_ZONEID: 1730 (void) printf("%*d", width, (int)psinfo->pr_zoneid); 1731 break; 1732 case F_ZONE: 1733 { 1734 char zonename[ZONENAME_MAX]; 1735 1736 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 1737 sizeof (zonename)) < 0) { 1738 (void) printf("%*d", width, 1739 ((int)psinfo->pr_zoneid)); 1740 } else { 1741 (void) printf("%*s", width, zonename); 1742 } 1743 } 1744 break; 1745 case F_CTID: 1746 if (psinfo->pr_contract == -1) 1747 (void) printf("%*s", width, "-"); 1748 else 1749 (void) printf("%*ld", width, (long)psinfo->pr_contract); 1750 break; 1751 } 1752 } 1753 1754 static void 1755 print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp) 1756 { 1757 int wcnt; 1758 int width = f->width; 1759 1760 switch (f->fname) { 1761 case F_FNAME: 1762 case F_COMM: 1763 case F_ARGS: 1764 /* 1765 * Print full width unless this is the last output format. 1766 */ 1767 wcnt = min(width, sizeof ("<defunct>")); 1768 if (f->next != NULL) 1769 (void) printf("%-*.*s", width, wcnt, "<defunct>"); 1770 else 1771 (void) printf("%-.*s", wcnt, "<defunct>"); 1772 break; 1773 1774 case F_PSR: 1775 case F_PCPU: 1776 case F_PMEM: 1777 case F_NICE: 1778 case F_CLASS: 1779 case F_STIME: 1780 case F_ETIME: 1781 case F_WCHAN: 1782 case F_PSET: 1783 (void) printf("%*s", width, "-"); 1784 break; 1785 1786 case F_OPRI: 1787 case F_PRI: 1788 case F_OSZ: 1789 case F_VSZ: 1790 case F_RSS: 1791 (void) printf("%*d", width, 0); 1792 break; 1793 1794 default: 1795 print_field(psinfo, f, ttyp); 1796 break; 1797 } 1798 } 1799 1800 static void 1801 pr_fields(psinfo_t *psinfo, const char *ttyp, 1802 void (*print_fld)(psinfo_t *, struct field *, const char *)) 1803 { 1804 struct field *f; 1805 1806 for (f = fields; f != NULL; f = f->next) { 1807 print_fld(psinfo, f, ttyp); 1808 if (f->next != NULL) 1809 (void) printf(" "); 1810 } 1811 (void) printf("\n"); 1812 } 1813 1814 /* 1815 * Returns 1 if arg is found in array arr, of length num; 0 otherwise. 1816 */ 1817 static int 1818 search(pid_t *arr, int number, pid_t arg) 1819 { 1820 int i; 1821 1822 for (i = 0; i < number; i++) 1823 if (arg == arr[i]) 1824 return (1); 1825 return (0); 1826 } 1827 1828 /* 1829 * Add an entry (user, group) to the specified table. 1830 */ 1831 static void 1832 add_ugentry(struct ughead *tbl, char *name) 1833 { 1834 struct ugdata *entp; 1835 1836 if (tbl->size == tbl->nent) { /* reallocate the table entries */ 1837 if ((tbl->size *= 2) == 0) 1838 tbl->size = 32; /* first time */ 1839 tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata)); 1840 } 1841 entp = &tbl->ent[tbl->nent++]; 1842 entp->id = 0; 1843 (void) strncpy(entp->name, name, MAXUGNAME); 1844 entp->name[MAXUGNAME] = '\0'; 1845 } 1846 1847 static int 1848 uconv(struct ughead *uhead) 1849 { 1850 struct ugdata *utbl = uhead->ent; 1851 int n = uhead->nent; 1852 struct passwd *pwd; 1853 int i; 1854 int fnd = 0; 1855 uid_t uid; 1856 1857 /* 1858 * Ask the name service for names. 1859 */ 1860 for (i = 0; i < n; i++) { 1861 /* 1862 * If name is numeric, ask for numeric id 1863 */ 1864 if (str2id(utbl[i].name, &uid, 0, UID_MAX) == 0) 1865 pwd = getpwuid(uid); 1866 else 1867 pwd = getpwnam(utbl[i].name); 1868 1869 /* 1870 * If found, enter found index into tbl array. 1871 */ 1872 if (pwd == NULL) { 1873 (void) fprintf(stderr, 1874 gettext("ps: unknown user %s\n"), utbl[i].name); 1875 continue; 1876 } 1877 1878 utbl[fnd].id = pwd->pw_uid; 1879 (void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME); 1880 fnd++; 1881 } 1882 1883 uhead->nent = fnd; /* in case it changed */ 1884 return (n - fnd); 1885 } 1886 1887 static int 1888 gconv(struct ughead *ghead) 1889 { 1890 struct ugdata *gtbl = ghead->ent; 1891 int n = ghead->nent; 1892 struct group *grp; 1893 gid_t gid; 1894 int i; 1895 int fnd = 0; 1896 1897 /* 1898 * Ask the name service for names. 1899 */ 1900 for (i = 0; i < n; i++) { 1901 /* 1902 * If name is numeric, ask for numeric id 1903 */ 1904 if (str2id(gtbl[i].name, &gid, 0, UID_MAX) == 0) 1905 grp = getgrgid(gid); 1906 else 1907 grp = getgrnam(gtbl[i].name); 1908 /* 1909 * If found, enter found index into tbl array. 1910 */ 1911 if (grp == NULL) { 1912 (void) fprintf(stderr, 1913 gettext("ps: unknown group %s\n"), gtbl[i].name); 1914 continue; 1915 } 1916 1917 gtbl[fnd].id = grp->gr_gid; 1918 (void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME); 1919 fnd++; 1920 } 1921 1922 ghead->nent = fnd; /* in case it changed */ 1923 return (n - fnd); 1924 } 1925 1926 /* 1927 * Return 1 if puid is in table, otherwise 0. 1928 */ 1929 static int 1930 ugfind(id_t id, struct ughead *ughead) 1931 { 1932 struct ugdata *utbl = ughead->ent; 1933 int n = ughead->nent; 1934 int i; 1935 1936 for (i = 0; i < n; i++) 1937 if (utbl[i].id == id) 1938 return (1); 1939 return (0); 1940 } 1941 1942 /* 1943 * Print starting time of process unless process started more than 24 hours 1944 * ago, in which case the date is printed. The date is printed in the form 1945 * "MMM dd" if old format, else the blank is replaced with an '_' so 1946 * it appears as a single word (for parseability). 1947 */ 1948 static void 1949 prtime(timestruc_t st, int width, int old) 1950 { 1951 char sttim[26]; 1952 time_t starttime; 1953 1954 starttime = st.tv_sec; 1955 if (st.tv_nsec > 500000000) 1956 starttime++; 1957 if ((now.tv_sec - starttime) >= 24*60*60) { 1958 (void) strftime(sttim, sizeof (sttim), old? \ 1959 "%b %d" : "%b_%d", localtime(&starttime)); 1960 sttim[7] = '\0'; 1961 } else { 1962 (void) strftime(sttim, sizeof (sttim), \ 1963 "%H:%M:%S", localtime(&starttime)); 1964 sttim[8] = '\0'; 1965 } 1966 (void) printf("%*.*s", width, width, sttim); 1967 } 1968 1969 static void 1970 przom(psinfo_t *psinfo) 1971 { 1972 long tm; 1973 struct passwd *pwd; 1974 char zonename[ZONENAME_MAX]; 1975 1976 /* 1977 * All fields before 'PID' are printed with a trailing space as a 1978 * spearator, rather than keeping track of which column is first. All 1979 * other fields are printed with a leading space. 1980 */ 1981 if (lflg) { /* F S */ 1982 if (!yflg) 1983 (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ 1984 (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ 1985 } 1986 if (Zflg) { 1987 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 1988 sizeof (zonename)) < 0) { 1989 (void) printf("%7.7d ", ((int)psinfo->pr_zoneid)); 1990 } else { 1991 (void) printf("%8.8s ", zonename); 1992 } 1993 } 1994 if (fflg) { 1995 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 1996 (void) printf("%8.8s ", pwd->pw_name); 1997 else 1998 (void) printf("%7.7d ", (int)psinfo->pr_euid); 1999 } else if (lflg) 2000 (void) printf("%6d ", (int)psinfo->pr_euid); 2001 2002 (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ 2003 if (lflg || fflg) 2004 (void) printf(" %*d", pidwidth, 2005 (int)psinfo->pr_ppid); /* PPID */ 2006 2007 if (jflg) { 2008 (void) printf(" %*d", pidwidth, 2009 (int)psinfo->pr_pgid); /* PGID */ 2010 (void) printf(" %*d", pidwidth, 2011 (int)psinfo->pr_sid); /* SID */ 2012 } 2013 2014 if (Lflg) 2015 (void) printf(" %5d", 0); /* LWP */ 2016 if (Pflg) 2017 (void) printf(" -"); /* PSR */ 2018 if (Lflg && fflg) 2019 (void) printf(" %5d", 0); /* NLWP */ 2020 2021 if (cflg) { 2022 (void) printf(" %4s", "-"); /* zombies have no class */ 2023 (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ 2024 } else if (lflg || fflg) { 2025 (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ 2026 if (lflg) 2027 (void) printf(" %3d %2s", 2028 psinfo->pr_lwp.pr_oldpri, "-"); /* PRI NI */ 2029 } 2030 if (lflg) { 2031 if (yflg) /* RSS SZ WCHAN */ 2032 (void) printf(" %5d %6d %8s", 0, 0, "-"); 2033 else /* ADDR SZ WCHAN */ 2034 (void) printf(" %8s %6d %8s", "-", 0, "-"); 2035 } 2036 if (fflg) 2037 (void) printf(" %8.8s", "-"); /* STIME */ 2038 (void) printf(" %-8.14s", "?"); /* TTY */ 2039 2040 tm = psinfo->pr_time.tv_sec; 2041 if (psinfo->pr_time.tv_nsec > 500000000) 2042 tm++; 2043 (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* TIME */ 2044 (void) printf(" <defunct>\n"); 2045 } 2046 2047 /* 2048 * Function to compute the number of printable bytes in a multibyte 2049 * command string ("internationalization"). 2050 */ 2051 static int 2052 namencnt(char *cmd, int csisize, int scrsize) 2053 { 2054 int csiwcnt = 0, scrwcnt = 0; 2055 int ncsisz, nscrsz; 2056 wchar_t wchar; 2057 int len; 2058 2059 while (*cmd != '\0') { 2060 if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX) 2061 len = MB_CUR_MAX; 2062 if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0) 2063 return (8); /* default to use for illegal chars */ 2064 if ((nscrsz = wcwidth(wchar)) <= 0) 2065 return (8); 2066 if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize) 2067 break; 2068 csiwcnt += ncsisz; 2069 scrwcnt += nscrsz; 2070 cmd += ncsisz; 2071 } 2072 return (csiwcnt); 2073 } 2074 2075 static char * 2076 err_string(int err) 2077 { 2078 static char buf[32]; 2079 char *str = strerror(err); 2080 2081 if (str == NULL) 2082 (void) snprintf(str = buf, sizeof (buf), "Errno #%d", err); 2083 2084 return (str); 2085 } 2086 2087 /* If allocation fails, die */ 2088 static void * 2089 Realloc(void *ptr, size_t size) 2090 { 2091 ptr = realloc(ptr, size); 2092 if (ptr == NULL) { 2093 (void) fprintf(stderr, gettext("ps: no memory\n")); 2094 exit(1); 2095 } 2096 return (ptr); 2097 } 2098 2099 static time_t 2100 delta_secs(const timestruc_t *start) 2101 { 2102 time_t seconds = now.tv_sec - start->tv_sec; 2103 long nanosecs = now.tv_usec * 1000 - start->tv_nsec; 2104 2105 if (nanosecs >= (NANOSEC / 2)) 2106 seconds++; 2107 else if (nanosecs < -(NANOSEC / 2)) 2108 seconds--; 2109 2110 return (seconds); 2111 } 2112 2113 /* 2114 * Returns the following: 2115 * 2116 * 0 No error 2117 * EINVAL Invalid number 2118 * ERANGE Value exceeds (min, max) range 2119 */ 2120 static int 2121 str2id(const char *p, pid_t *val, long min, long max) 2122 { 2123 char *q; 2124 long number; 2125 int error; 2126 2127 errno = 0; 2128 number = strtol(p, &q, 10); 2129 2130 if (errno != 0 || q == p || *q != '\0') { 2131 if ((error = errno) == 0) { 2132 /* 2133 * strtol() can fail without setting errno, or it can 2134 * set it to EINVAL or ERANGE. In the case errno is 2135 * still zero, return EINVAL. 2136 */ 2137 error = EINVAL; 2138 } 2139 } else if (number < min || number > max) { 2140 error = ERANGE; 2141 } else { 2142 error = 0; 2143 } 2144 2145 *val = number; 2146 2147 return (error); 2148 } 2149 2150 static int 2151 pidcmp(const void *p1, const void *p2) 2152 { 2153 pid_t i = *((pid_t *)p1); 2154 pid_t j = *((pid_t *)p2); 2155 2156 return (i - j); 2157 } 2158