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