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