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