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