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