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