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