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