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