1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * Portions Copyright 2009 Chad Mynhier
  27  * Copyright 2013 Joyent, Inc.  All rights reserved.
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/resource.h>
  32 #include <sys/loadavg.h>
  33 #include <sys/time.h>
  34 #include <sys/pset.h>
  35 #include <sys/vm_usage.h>
  36 #include <sys/queue.h>
  37 #include <zone.h>
  38 #include <libzonecfg.h>
  39 
  40 #include <stdio.h>
  41 #include <stdlib.h>
  42 #include <unistd.h>
  43 #include <dirent.h>
  44 #include <string.h>
  45 #include <strings.h>
  46 #include <errno.h>
  47 #include <poll.h>
  48 #include <ctype.h>
  49 #include <fcntl.h>
  50 #include <limits.h>
  51 #include <signal.h>
  52 #include <time.h>
  53 #include <project.h>
  54 
  55 #include <langinfo.h>
  56 #include <libintl.h>
  57 #include <locale.h>
  58 
  59 #include <sasl/saslutil.h>
  60 
  61 #include "prstat.h"
  62 #include "prutil.h"
  63 #include "prtable.h"
  64 #include "prsort.h"
  65 #include "prfile.h"
  66 
  67 /*
  68  * x86 <sys/regs.h> ERR conflicts with <curses.h> ERR.  For the purposes
  69  * of this file, we care about the curses.h ERR so include that last.
  70  */
  71 
  72 #if     defined(ERR)
  73 #undef  ERR
  74 #endif
  75 
  76 #ifndef TEXT_DOMAIN                     /* should be defined by cc -D */
  77 #define TEXT_DOMAIN     "SYS_TEST"      /* use this only if it wasn't */
  78 #endif
  79 
  80 #include <curses.h>
  81 #include <term.h>
  82 
  83 #define PSINFO_HEADER_PROC \
  84 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP       "
  85 #define PSINFO_HEADER_PROC_LGRP \
  86 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/NLWP  "
  87 #define PSINFO_HEADER_LWP \
  88 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/LWPID      "
  89 #define PSINFO_HEADER_LWP_LGRP \
  90 "   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU LGRP PROCESS/LWPID "
  91 #define USAGE_HEADER_PROC \
  92 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP  "
  93 #define USAGE_HEADER_LWP \
  94 "   PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWPID "
  95 #define USER_HEADER_PROC \
  96 " NPROC USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
  97 #define USER_HEADER_LWP \
  98 "  NLWP USERNAME  SWAP   RSS MEMORY      TIME  CPU                             "
  99 #define TASK_HEADER_PROC \
 100 "TASKID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 101 #define TASK_HEADER_LWP \
 102 "TASKID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 103 #define PROJECT_HEADER_PROC \
 104 "PROJID    NPROC  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 105 #define PROJECT_HEADER_LWP \
 106 "PROJID     NLWP  SWAP   RSS MEMORY      TIME  CPU PROJECT                     "
 107 #define ZONE_HEADER_PROC \
 108 "ZONEID    NPROC  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 109 #define ZONE_HEADER_LWP \
 110 "ZONEID     NLWP  SWAP   RSS MEMORY      TIME  CPU ZONE                        "
 111 #define PSINFO_LINE \
 112 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %-.16s/%d"
 113 #define PSINFO_LINE_LGRP \
 114 "%6d %-8s %5s %5s %-6s %3s  %3s %9s %3.3s%% %4d %-.16s/%d"
 115 #define USAGE_LINE \
 116 "%6d %-8s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s %3.3s "\
 117 "%3.3s %-.12s/%d"
 118 #define USER_LINE \
 119 "%6d %-8s %5.5s %5.5s   %3.3s%% %9s %3.3s%%"
 120 #define TASK_LINE \
 121 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 122 #define PROJECT_LINE \
 123 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %28s"
 124 #define ZONE_LINE \
 125 "%6d %8d %5s %5s   %3.3s%% %9s %3.3s%% %-28s"
 126 
 127 #define TOTAL_LINE \
 128 "Total: %d processes, %d lwps, load averages: %3.2f, %3.2f, %3.2f"
 129 
 130 /* global variables */
 131 
 132 static char     *t_ulon;                        /* termcap: start underline */
 133 static char     *t_uloff;                       /* termcap: end underline */
 134 static char     *t_up;                          /* termcap: cursor 1 line up */
 135 static char     *t_eol;                         /* termcap: clear end of line */
 136 static char     *t_smcup;                       /* termcap: cursor mvcap on */
 137 static char     *t_rmcup;                       /* termcap: cursor mvcap off */
 138 static char     *t_home;                        /* termcap: move cursor home */
 139 static char     *movecur = NULL;                /* termcap: move up string */
 140 static char     *empty_string = "\0";           /* termcap: empty string */
 141 static uint_t   print_movecur = FALSE;          /* print movecur or not */
 142 static int      is_curses_on = FALSE;           /* current curses state */
 143 
 144 static table_t  pid_tbl = {0, 0, NULL};         /* selected processes */
 145 static table_t  cpu_tbl = {0, 0, NULL};         /* selected processors */
 146 static table_t  set_tbl = {0, 0, NULL};         /* selected processor sets */
 147 static table_t  prj_tbl = {0, 0, NULL};         /* selected projects */
 148 static table_t  tsk_tbl = {0, 0, NULL};         /* selected tasks */
 149 static table_t  lgr_tbl = {0, 0, NULL};         /* selected lgroups */
 150 static zonetbl_t zone_tbl = {0, 0, NULL};       /* selected zones */
 151 static uidtbl_t euid_tbl = {0, 0, NULL};        /* selected effective users */
 152 static uidtbl_t ruid_tbl = {0, 0, NULL};        /* selected real users */
 153 
 154 static uint_t   total_procs;                    /* total number of procs */
 155 static uint_t   total_lwps;                     /* total number of lwps */
 156 static float    total_cpu;                      /* total cpu usage */
 157 static float    total_mem;                      /* total memory usage */
 158 
 159 static list_t   lwps;                           /* list of lwps/processes */
 160 static list_t   users;                          /* list of users */
 161 static list_t   tasks;                          /* list of tasks */
 162 static list_t   projects;                       /* list of projects */
 163 static list_t   zones;                          /* list of zones */
 164 static list_t   lgroups;                        /* list of lgroups */
 165 
 166 static volatile uint_t sigwinch = 0;
 167 static volatile uint_t sigtstp = 0;
 168 static volatile uint_t sigterm = 0;
 169 
 170 static long pagesize;
 171 
 172 /*
 173  * Cache for mapping 'alias' names (an attribute of the zone configuration) to
 174  * zone names, for use in -Z output.
 175  */
 176 static uint_t enable_alias_cache = FALSE;
 177 static SLIST_HEAD(alias_cache_head, alias_cache_entry) alias_cache =
 178     SLIST_HEAD_INITIALIZER(alias_cache);
 179 
 180 typedef struct alias_cache_entry {
 181         char *ace_zonename;
 182         char *ace_alias;
 183 
 184         SLIST_ENTRY(alias_cache_entry) ace_linkage;
 185 } alias_cache_entry_t;
 186 
 187 static const char *lookup_zone_alias(const char *);
 188 
 189 /* default settings */
 190 
 191 static optdesc_t opts = {
 192         5,                      /* interval between updates, seconds */
 193         15,                     /* number of lines in top part */
 194         5,                      /* number of lines in bottom part */
 195         -1,                     /* number of iterations; infinitely */
 196         OPT_PSINFO | OPT_FULLSCREEN | OPT_USEHOME | OPT_TERMCAP,
 197         -1                      /* sort in decreasing order */
 198 };
 199 
 200 /*
 201  * Print timestamp as decimal reprentation of time_t value (-d u was specified)
 202  * or the standard date format (-d d was specified).
 203  */
 204 static void
 205 print_timestamp(void)
 206 {
 207         time_t t = time(NULL);
 208         static char *fmt = NULL;
 209 
 210         /* We only need to retrieve this once per invocation */
 211         if (fmt == NULL)
 212                 fmt = nl_langinfo(_DATE_FMT);
 213 
 214         if (opts.o_outpmode & OPT_UDATE) {
 215                 (void) printf("%ld", t);
 216         } else if (opts.o_outpmode & OPT_DDATE) {
 217                 char dstr[64];
 218                 int len;
 219 
 220                 len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
 221                 if (len > 0)
 222                         (void) printf("%s", dstr);
 223         }
 224         (void) putp(t_eol);
 225         (void) putchar('\n');
 226 }
 227 
 228 static void
 229 psetloadavg(long psetid, void *ptr)
 230 {
 231         double psetloadavg[3];
 232         double *loadavg = ptr;
 233 
 234         if (pset_getloadavg((psetid_t)psetid, psetloadavg, 3) != -1) {
 235                 *loadavg++ += psetloadavg[0];
 236                 *loadavg++ += psetloadavg[1];
 237                 *loadavg += psetloadavg[2];
 238         }
 239 }
 240 
 241 /*
 242  * Queries the memory virtual and rss size for each member of a list.
 243  * This will override the values computed by /proc aggregation.
 244  */
 245 static void
 246 list_getsize(list_t *list)
 247 {
 248         id_info_t *id;
 249         vmusage_t *results, *next;
 250         vmusage_t *match;
 251         size_t nres = 0;
 252         size_t i;
 253         uint_t flags = 0;
 254         int ret;
 255         size_t physmem;
 256 
 257         if (!(opts.o_outpmode & OPT_VMUSAGE))
 258                 return;
 259 
 260         physmem = sysconf(_SC_PHYS_PAGES) * pagesize;
 261 
 262         /*
 263          * Determine what swap/rss results to calculate.  getvmusage() will
 264          * prune results returned to non-global zones automatically, so
 265          * there is no need to pass different flags when calling from a
 266          * non-global zone.
 267          *
 268          * Currently list_getsize() is only called with a single flag.  This
 269          * is because -Z, -J, -T, and -a are mutually exclusive.  Regardless
 270          * of this, we handle multiple flags.
 271          */
 272         if (opts.o_outpmode & OPT_USERS) {
 273                 /*
 274                  * Gather rss for all users in all zones.  Treat the same
 275                  * uid in different zones as the same user.
 276                  */
 277                 flags |= VMUSAGE_COL_RUSERS;
 278 
 279         } else if (opts.o_outpmode & OPT_TASKS) {
 280                 /* Gather rss for all tasks in all zones */
 281                 flags |= VMUSAGE_ALL_TASKS;
 282 
 283         } else if (opts.o_outpmode & OPT_PROJECTS) {
 284                 /*
 285                  * Gather rss for all projects in all zones.  Treat the same
 286                  * projid in diffrent zones as the same project.
 287                  */
 288                 flags |= VMUSAGE_COL_PROJECTS;
 289 
 290         } else if (opts.o_outpmode & OPT_ZONES) {
 291                 /* Gather rss for all zones */
 292                 flags |= VMUSAGE_ALL_ZONES;
 293 
 294         } else {
 295                 Die(gettext(
 296                     "Cannot determine rss flags for output options %x\n"),
 297                     opts.o_outpmode);
 298         }
 299 
 300         /*
 301          * getvmusage() returns an array of result structures.  One for
 302          * each zone, project, task, or user on the system, depending on
 303          * flags.
 304          *
 305          * If getvmusage() fails, prstat will use the size already gathered
 306          * from psinfo
 307          */
 308         if (getvmusage(flags, opts.o_interval, NULL, &nres) != 0)
 309                 return;
 310 
 311         results = (vmusage_t *)Malloc(sizeof (vmusage_t) * nres);
 312         for (;;) {
 313                 ret = getvmusage(flags, opts.o_interval, results, &nres);
 314                 if (ret == 0)
 315                         break;
 316                 if (errno == EOVERFLOW) {
 317                         results = (vmusage_t *)Realloc(results,
 318                             sizeof (vmusage_t) * nres);
 319                         continue;
 320                 }
 321                 /*
 322                  * Failure for some other reason.  Prstat will use the size
 323                  * already gathered from psinfo.
 324                  */
 325                 free(results);
 326                 return;
 327         }
 328         for (id = list->l_head; id != NULL; id = id->id_next) {
 329 
 330                 match = NULL;
 331                 next = results;
 332                 for (i = 0; i < nres; i++, next++) {
 333                         switch (flags) {
 334                         case VMUSAGE_COL_RUSERS:
 335                                 if (next->vmu_id == id->id_uid)
 336                                         match = next;
 337                                 break;
 338                         case VMUSAGE_ALL_TASKS:
 339                                 if (next->vmu_id == id->id_taskid)
 340                                         match = next;
 341                                 break;
 342                         case VMUSAGE_COL_PROJECTS:
 343                                 if (next->vmu_id == id->id_projid)
 344                                         match = next;
 345                                 break;
 346                         case VMUSAGE_ALL_ZONES:
 347                                 if (next->vmu_id == id->id_zoneid)
 348                                         match = next;
 349                                 break;
 350                         default:
 351                                 Die(gettext(
 352                                     "Unknown vmusage flags %d\n"), flags);
 353                         }
 354                 }
 355                 if (match != NULL) {
 356                         id->id_size = match->vmu_swap_all / 1024;
 357                         id->id_rssize = match->vmu_rss_all / 1024;
 358                         id->id_pctmem = (100.0 * (float)match->vmu_rss_all) /
 359                             (float)physmem;
 360                         /* Output using data from getvmusage() */
 361                         id->id_sizematch = B_TRUE;
 362                 }
 363                 /*
 364                  * If no match is found, prstat will use the size already
 365                  * gathered from psinfo.
 366                  */
 367         }
 368         free(results);
 369 }
 370 
 371 /*
 372  * A routine to display the contents of the list on the screen
 373  */
 374 static void
 375 list_print(list_t *list)
 376 {
 377         lwp_info_t *lwp;
 378         id_info_t *id;
 379         char usr[4], sys[4], trp[4], tfl[4];
 380         char dfl[4], lck[4], slp[4], lat[4];
 381         char vcx[4], icx[4], scl[4], sig[4];
 382         char psize[6], prssize[6], pmem[6], pcpu[6], ptime[12];
 383         char pstate[7], pnice[4], ppri[4];
 384         char pname[LOGNAME_MAX+1];
 385         char projname[PROJNAME_MAX+1];
 386         char zonename[ZONENAME_MAX+1];
 387         float cpu, mem;
 388         double loadavg[3] = {0, 0, 0};
 389         int i, lwpid;
 390 
 391         if (list->l_size == 0)
 392                 return;
 393 
 394         if (foreach_element(&set_tbl, &loadavg, psetloadavg) == 0) {
 395                 /*
 396                  * If processor sets aren't specified, we display system-wide
 397                  * load averages.
 398                  */
 399                 (void) getloadavg(loadavg, 3);
 400         }
 401 
 402         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)) &&
 403             ((list->l_type == LT_LWPS) || !(opts.o_outpmode & OPT_SPLIT)))
 404                 print_timestamp();
 405         if (opts.o_outpmode & OPT_TTY)
 406                 (void) putchar('\r');
 407         (void) putp(t_ulon);
 408 
 409         switch (list->l_type) {
 410         case LT_PROJECTS:
 411                 if (opts.o_outpmode & OPT_LWPS)
 412                         (void) printf(PROJECT_HEADER_LWP);
 413                 else
 414                         (void) printf(PROJECT_HEADER_PROC);
 415                 break;
 416         case LT_TASKS:
 417                 if (opts.o_outpmode & OPT_LWPS)
 418                         (void) printf(TASK_HEADER_LWP);
 419                 else
 420                         (void) printf(TASK_HEADER_PROC);
 421                 break;
 422         case LT_ZONES:
 423                 if (opts.o_outpmode & OPT_LWPS)
 424                         (void) printf(ZONE_HEADER_LWP);
 425                 else
 426                         (void) printf(ZONE_HEADER_PROC);
 427                 break;
 428         case LT_USERS:
 429                 if (opts.o_outpmode & OPT_LWPS)
 430                         (void) printf(USER_HEADER_LWP);
 431                 else
 432                         (void) printf(USER_HEADER_PROC);
 433                 break;
 434         case LT_LWPS:
 435                 if (opts.o_outpmode & OPT_LWPS) {
 436                         if (opts.o_outpmode & OPT_PSINFO) {
 437                                 if (opts.o_outpmode & OPT_LGRP)
 438                                         (void) printf(PSINFO_HEADER_LWP_LGRP);
 439                                 else
 440                                         (void) printf(PSINFO_HEADER_LWP);
 441                         }
 442                         if (opts.o_outpmode & OPT_MSACCT)
 443                                 (void) printf(USAGE_HEADER_LWP);
 444                 } else {
 445                         if (opts.o_outpmode & OPT_PSINFO) {
 446                                 if (opts.o_outpmode & OPT_LGRP)
 447                                         (void) printf(PSINFO_HEADER_PROC_LGRP);
 448                                 else
 449                                         (void) printf(PSINFO_HEADER_PROC);
 450                         }
 451                         if (opts.o_outpmode & OPT_MSACCT)
 452                                 (void) printf(USAGE_HEADER_PROC);
 453                 }
 454                 break;
 455         }
 456 
 457         (void) putp(t_uloff);
 458         (void) putp(t_eol);
 459         (void) putchar('\n');
 460 
 461         for (i = 0; i < list->l_used; i++) {
 462                 switch (list->l_type) {
 463                 case LT_PROJECTS:
 464                 case LT_TASKS:
 465                 case LT_USERS:
 466                 case LT_ZONES:
 467                         id = list->l_ptrs[i];
 468                         /*
 469                          * CPU usage and memory usage normalization
 470                          */
 471                         if (total_cpu >= 100)
 472                                 cpu = (100 * id->id_pctcpu) / total_cpu;
 473                         else
 474                                 cpu = id->id_pctcpu;
 475                         if (id->id_sizematch == B_FALSE && total_mem >= 100)
 476                                 mem = (100 * id->id_pctmem) / total_mem;
 477                         else
 478                                 mem = id->id_pctmem;
 479                         if (list->l_type == LT_USERS)
 480                                 pwd_getname(id->id_uid, pname, LOGNAME_MAX + 1,
 481                                     opts.o_outpmode & OPT_NORESOLVE);
 482                         else if (list->l_type == LT_ZONES)
 483                                 getzonename(id->id_zoneid, zonename,
 484                                     ZONENAME_MAX);
 485                         else
 486                                 getprojname(id->id_projid, projname,
 487                                     PROJNAME_MAX,
 488                                     opts.o_outpmode & OPT_NORESOLVE);
 489                         Format_size(psize, id->id_size, 6);
 490                         Format_size(prssize, id->id_rssize, 6);
 491                         Format_pct(pmem, mem, 4);
 492                         Format_pct(pcpu, cpu, 4);
 493                         Format_time(ptime, id->id_time, 10);
 494                         if (opts.o_outpmode & OPT_TTY)
 495                                 (void) putchar('\r');
 496                         if (list->l_type == LT_PROJECTS)
 497                                 (void) printf(PROJECT_LINE, (int)id->id_projid,
 498                                     id->id_nproc, psize, prssize, pmem, ptime,
 499                                     pcpu, projname);
 500                         else if (list->l_type == LT_TASKS)
 501                                 (void) printf(TASK_LINE, (int)id->id_taskid,
 502                                     id->id_nproc, psize, prssize, pmem, ptime,
 503                                     pcpu, projname);
 504                         else if (list->l_type == LT_ZONES)
 505                                 (void) printf(ZONE_LINE, (int)id->id_zoneid,
 506                                     id->id_nproc, psize, prssize, pmem, ptime,
 507                                     pcpu, lookup_zone_alias(zonename));
 508                         else
 509                                 (void) printf(USER_LINE, id->id_nproc, pname,
 510                                     psize, prssize, pmem, ptime, pcpu);
 511                         (void) putp(t_eol);
 512                         (void) putchar('\n');
 513                         break;
 514                 case LT_LWPS:
 515                         lwp = list->l_ptrs[i];
 516                         if (opts.o_outpmode & OPT_LWPS)
 517                                 lwpid = lwp->li_info.pr_lwp.pr_lwpid;
 518                         else
 519                                 lwpid = lwp->li_info.pr_nlwp +
 520                                     lwp->li_info.pr_nzomb;
 521                         pwd_getname(lwp->li_info.pr_uid, pname, LOGNAME_MAX + 1,
 522                             opts.o_outpmode & OPT_NORESOLVE);
 523                         if (opts.o_outpmode & OPT_PSINFO) {
 524                                 Format_size(psize, lwp->li_info.pr_size, 6);
 525                                 Format_size(prssize, lwp->li_info.pr_rssize, 6);
 526                                 Format_state(pstate,
 527                                     lwp->li_info.pr_lwp.pr_sname,
 528                                     lwp->li_info.pr_lwp.pr_onpro, 7);
 529                                 if (strcmp(lwp->li_info.pr_lwp.pr_clname,
 530                                     "RT") == 0 ||
 531                                     strcmp(lwp->li_info.pr_lwp.pr_clname,
 532                                     "SYS") == 0 ||
 533                                     lwp->li_info.pr_lwp.pr_sname == 'Z')
 534                                         (void) strcpy(pnice, "  -");
 535                                 else
 536                                         Format_num(pnice,
 537                                             lwp->li_info.pr_lwp.pr_nice - NZERO,
 538                                             4);
 539                                 Format_num(ppri, lwp->li_info.pr_lwp.pr_pri, 4);
 540                                 Format_pct(pcpu,
 541                                     FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu), 4);
 542                                 if (opts.o_outpmode & OPT_LWPS)
 543                                         Format_time(ptime,
 544                                             lwp->li_info.pr_lwp.pr_time.tv_sec,
 545                                             10);
 546                                 else
 547                                         Format_time(ptime,
 548                                             lwp->li_info.pr_time.tv_sec, 10);
 549                                 if (opts.o_outpmode & OPT_TTY)
 550                                         (void) putchar('\r');
 551                                 stripfname(lwp->li_info.pr_fname);
 552                                 if (opts.o_outpmode & OPT_LGRP) {
 553                                         (void) printf(PSINFO_LINE_LGRP,
 554                                             (int)lwp->li_info.pr_pid, pname,
 555                                             psize, prssize, pstate, ppri, pnice,
 556                                             ptime, pcpu,
 557                                             (int)lwp->li_info.pr_lwp.pr_lgrp,
 558                                             lwp->li_info.pr_fname, lwpid);
 559                                 } else {
 560                                         (void) printf(PSINFO_LINE,
 561                                             (int)lwp->li_info.pr_pid, pname,
 562                                             psize, prssize, pstate, ppri, pnice,
 563                                             ptime, pcpu,
 564                                             lwp->li_info.pr_fname, lwpid);
 565                                 }
 566                                 (void) putp(t_eol);
 567                                 (void) putchar('\n');
 568                         }
 569                         if (opts.o_outpmode & OPT_MSACCT) {
 570                                 Format_pct(usr, lwp->li_usr, 4);
 571                                 Format_pct(sys, lwp->li_sys, 4);
 572                                 Format_pct(slp, lwp->li_slp, 4);
 573                                 Format_num(vcx, lwp->li_vcx, 4);
 574                                 Format_num(icx, lwp->li_icx, 4);
 575                                 Format_num(scl, lwp->li_scl, 4);
 576                                 Format_num(sig, lwp->li_sig, 4);
 577                                 Format_pct(trp, lwp->li_trp, 4);
 578                                 Format_pct(tfl, lwp->li_tfl, 4);
 579                                 Format_pct(dfl, lwp->li_dfl, 4);
 580                                 Format_pct(lck, lwp->li_lck, 4);
 581                                 Format_pct(lat, lwp->li_lat, 4);
 582                                 if (opts.o_outpmode & OPT_TTY)
 583                                         (void) putchar('\r');
 584                                 stripfname(lwp->li_info.pr_fname);
 585                                 (void) printf(USAGE_LINE,
 586                                     (int)lwp->li_info.pr_pid, pname,
 587                                     usr, sys, trp, tfl, dfl, lck,
 588                                     slp, lat, vcx, icx, scl, sig,
 589                                     lwp->li_info.pr_fname, lwpid);
 590                                 (void) putp(t_eol);
 591                                 (void) putchar('\n');
 592                         }
 593                         break;
 594                 }
 595         }
 596 
 597         if (opts.o_outpmode & OPT_TTY)
 598                 (void) putchar('\r');
 599         if (opts.o_outpmode & OPT_TERMCAP) {
 600                 switch (list->l_type) {
 601                 case LT_PROJECTS:
 602                 case LT_USERS:
 603                 case LT_TASKS:
 604                 case LT_ZONES:
 605                         while (i++ < opts.o_nbottom) {
 606                                 (void) putp(t_eol);
 607                                 (void) putchar('\n');
 608                         }
 609                         break;
 610                 case LT_LWPS:
 611                         while (i++ < opts.o_ntop) {
 612                                 (void) putp(t_eol);
 613                                 (void) putchar('\n');
 614                         }
 615                 }
 616         }
 617 
 618         if (opts.o_outpmode & OPT_TTY)
 619                 (void) putchar('\r');
 620 
 621         if ((opts.o_outpmode & OPT_SPLIT) && list->l_type == LT_LWPS)
 622                 return;
 623 
 624         (void) printf(TOTAL_LINE, total_procs, total_lwps,
 625             loadavg[LOADAVG_1MIN], loadavg[LOADAVG_5MIN],
 626             loadavg[LOADAVG_15MIN]);
 627         (void) putp(t_eol);
 628         (void) putchar('\n');
 629         if (opts.o_outpmode & OPT_TTY)
 630                 (void) putchar('\r');
 631         (void) putp(t_eol);
 632         (void) fflush(stdout);
 633 }
 634 
 635 static lwp_info_t *
 636 list_add_lwp(list_t *list, pid_t pid, id_t lwpid)
 637 {
 638         lwp_info_t *lwp;
 639 
 640         if (list->l_head == NULL) {
 641                 list->l_head = list->l_tail = lwp = Zalloc(sizeof (lwp_info_t));
 642         } else {
 643                 lwp = Zalloc(sizeof (lwp_info_t));
 644                 lwp->li_prev = list->l_tail;
 645                 ((lwp_info_t *)list->l_tail)->li_next = lwp;
 646                 list->l_tail = lwp;
 647         }
 648         lwp->li_info.pr_pid = pid;
 649         lwp->li_info.pr_lwp.pr_lwpid = lwpid;
 650         lwpid_add(lwp, pid, lwpid);
 651         list->l_count++;
 652         return (lwp);
 653 }
 654 
 655 static void
 656 list_remove_lwp(list_t *list, lwp_info_t *lwp)
 657 {
 658         if (lwp->li_prev)
 659                 lwp->li_prev->li_next = lwp->li_next;
 660         else
 661                 list->l_head = lwp->li_next;      /* removing the head */
 662         if (lwp->li_next)
 663                 lwp->li_next->li_prev = lwp->li_prev;
 664         else
 665                 list->l_tail = lwp->li_prev;      /* removing the tail */
 666         lwpid_del(lwp->li_info.pr_pid, lwp->li_info.pr_lwp.pr_lwpid);
 667         if (lwpid_pidcheck(lwp->li_info.pr_pid) == 0)
 668                 fds_rm(lwp->li_info.pr_pid);
 669         list->l_count--;
 670         free(lwp);
 671 }
 672 
 673 static void
 674 list_clear(list_t *list)
 675 {
 676         if (list->l_type == LT_LWPS) {
 677                 lwp_info_t      *lwp = list->l_tail;
 678                 lwp_info_t      *lwp_tmp;
 679 
 680                 fd_closeall();
 681                 while (lwp) {
 682                         lwp_tmp = lwp;
 683                         lwp = lwp->li_prev;
 684                         list_remove_lwp(&lwps, lwp_tmp);
 685                 }
 686         } else {
 687                 id_info_t *id = list->l_head;
 688                 id_info_t *nextid;
 689 
 690                 while (id) {
 691                         nextid = id->id_next;
 692                         free(id);
 693                         id = nextid;
 694                 }
 695                 list->l_count = 0;
 696                 list->l_head = list->l_tail = NULL;
 697         }
 698 }
 699 
 700 static void
 701 list_update(list_t *list, lwp_info_t *lwp)
 702 {
 703         id_info_t *id;
 704 
 705         if (list->l_head == NULL) {                  /* first element */
 706                 list->l_head = list->l_tail = id = Zalloc(sizeof (id_info_t));
 707                 goto update;
 708         }
 709 
 710         for (id = list->l_head; id; id = id->id_next) {
 711                 if ((list->l_type == LT_USERS) &&
 712                     (id->id_uid != lwp->li_info.pr_uid))
 713                         continue;
 714                 if ((list->l_type == LT_TASKS) &&
 715                     (id->id_taskid != lwp->li_info.pr_taskid))
 716                         continue;
 717                 if ((list->l_type == LT_PROJECTS) &&
 718                     (id->id_projid != lwp->li_info.pr_projid))
 719                         continue;
 720                 if ((list->l_type == LT_ZONES) &&
 721                     (id->id_zoneid != lwp->li_info.pr_zoneid))
 722                         continue;
 723                 if ((list->l_type == LT_LGRPS) &&
 724                     (id->id_lgroup != lwp->li_info.pr_lwp.pr_lgrp))
 725                         continue;
 726                 id->id_nproc++;
 727                 id->id_taskid        = lwp->li_info.pr_taskid;
 728                 id->id_projid        = lwp->li_info.pr_projid;
 729                 id->id_zoneid        = lwp->li_info.pr_zoneid;
 730                 id->id_lgroup        = lwp->li_info.pr_lwp.pr_lgrp;
 731 
 732                 if (lwp->li_flags & LWP_REPRESENT) {
 733                         id->id_size  += lwp->li_info.pr_size;
 734                         id->id_rssize        += lwp->li_info.pr_rssize;
 735                 }
 736                 id->id_pctcpu        += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
 737                 if (opts.o_outpmode & OPT_LWPS)
 738                         id->id_time += TIME2SEC(lwp->li_info.pr_lwp.pr_time);
 739                 else
 740                         id->id_time += TIME2SEC(lwp->li_info.pr_time);
 741                 id->id_pctmem        += FRC2PCT(lwp->li_info.pr_pctmem);
 742                 id->id_key   += lwp->li_key;
 743                 total_cpu       += FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
 744                 total_mem       += FRC2PCT(lwp->li_info.pr_pctmem);
 745                 return;
 746         }
 747 
 748         id = list->l_tail;
 749         id->id_next = Zalloc(sizeof (id_info_t));
 750         id->id_next->id_prev = list->l_tail;
 751         id->id_next->id_next = NULL;
 752         list->l_tail = id->id_next;
 753         id = list->l_tail;
 754 update:
 755         id->id_uid   = lwp->li_info.pr_uid;
 756         id->id_projid        = lwp->li_info.pr_projid;
 757         id->id_taskid        = lwp->li_info.pr_taskid;
 758         id->id_zoneid        = lwp->li_info.pr_zoneid;
 759         id->id_lgroup        = lwp->li_info.pr_lwp.pr_lgrp;
 760         id->id_nproc++;
 761         id->id_sizematch = B_FALSE;
 762         if (lwp->li_flags & LWP_REPRESENT) {
 763                 id->id_size  = lwp->li_info.pr_size;
 764                 id->id_rssize        = lwp->li_info.pr_rssize;
 765         }
 766         id->id_pctcpu        = FRC2PCT(lwp->li_info.pr_lwp.pr_pctcpu);
 767         if (opts.o_outpmode & OPT_LWPS)
 768                 id->id_time = TIME2SEC(lwp->li_info.pr_lwp.pr_time);
 769         else
 770                 id->id_time = TIME2SEC(lwp->li_info.pr_time);
 771         id->id_pctmem        = FRC2PCT(lwp->li_info.pr_pctmem);
 772         id->id_key   = lwp->li_key;
 773         total_cpu       += id->id_pctcpu;
 774         total_mem       += id->id_pctmem;
 775         list->l_count++;
 776 }
 777 
 778 static void
 779 lwp_update(lwp_info_t *lwp, pid_t pid, id_t lwpid, struct prusage *usage)
 780 {
 781         float period;
 782 
 783         if (!lwpid_is_active(pid, lwpid)) {
 784                 /*
 785                  * If we are reading cpu times for the first time then
 786                  * calculate average cpu times based on whole process
 787                  * execution time.
 788                  */
 789                 (void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
 790                 period = TIME2NSEC(usage->pr_rtime);
 791                 period = period/(float)100;
 792 
 793                 if (period == 0) { /* zombie */
 794                         period = 1;
 795                         lwp->li_usr = 0;
 796                         lwp->li_sys = 0;
 797                         lwp->li_slp = 0;
 798                 } else {
 799                         lwp->li_usr = TIME2NSEC(usage->pr_utime)/period;
 800                         lwp->li_sys = TIME2NSEC(usage->pr_stime)/period;
 801                         lwp->li_slp = TIME2NSEC(usage->pr_slptime)/period;
 802                 }
 803                 lwp->li_trp = TIME2NSEC(usage->pr_ttime)/period;
 804                 lwp->li_tfl = TIME2NSEC(usage->pr_tftime)/period;
 805                 lwp->li_dfl = TIME2NSEC(usage->pr_dftime)/period;
 806                 lwp->li_lck = TIME2NSEC(usage->pr_ltime)/period;
 807                 lwp->li_lat = TIME2NSEC(usage->pr_wtime)/period;
 808                 period = (period / NANOSEC)*(float)100; /* now in seconds */
 809                 lwp->li_vcx = (ulong_t)
 810                     (opts.o_interval * (usage->pr_vctx/period));
 811                 lwp->li_icx = (ulong_t)
 812                     (opts.o_interval * (usage->pr_ictx/period));
 813                 lwp->li_scl = (ulong_t)
 814                     (opts.o_interval * (usage->pr_sysc/period));
 815                 lwp->li_sig = (ulong_t)
 816                     (opts.o_interval * (usage->pr_sigs/period));
 817                 (void) lwpid_set_active(pid, lwpid);
 818         } else {
 819                 /*
 820                  * If this is not a first time we are reading a process's
 821                  * CPU times then recalculate CPU times based on fresh data
 822                  * obtained from procfs and previous CPU time usage values.
 823                  */
 824                 period = TIME2NSEC(usage->pr_rtime)-
 825                     TIME2NSEC(lwp->li_usage.pr_rtime);
 826                 period = period/(float)100;
 827 
 828                 if (period == 0) { /* zombie */
 829                         period = 1;
 830                         lwp->li_usr = 0;
 831                         lwp->li_sys = 0;
 832                         lwp->li_slp = 0;
 833                 } else {
 834                         lwp->li_usr = (TIME2NSEC(usage->pr_utime)-
 835                             TIME2NSEC(lwp->li_usage.pr_utime))/period;
 836                         lwp->li_sys = (TIME2NSEC(usage->pr_stime) -
 837                             TIME2NSEC(lwp->li_usage.pr_stime))/period;
 838                         lwp->li_slp = (TIME2NSEC(usage->pr_slptime) -
 839                             TIME2NSEC(lwp->li_usage.pr_slptime))/period;
 840                 }
 841                 lwp->li_trp = (TIME2NSEC(usage->pr_ttime) -
 842                     TIME2NSEC(lwp->li_usage.pr_ttime))/period;
 843                 lwp->li_tfl = (TIME2NSEC(usage->pr_tftime) -
 844                     TIME2NSEC(lwp->li_usage.pr_tftime))/period;
 845                 lwp->li_dfl = (TIME2NSEC(usage->pr_dftime) -
 846                     TIME2NSEC(lwp->li_usage.pr_dftime))/period;
 847                 lwp->li_lck = (TIME2NSEC(usage->pr_ltime) -
 848                     TIME2NSEC(lwp->li_usage.pr_ltime))/period;
 849                 lwp->li_lat = (TIME2NSEC(usage->pr_wtime) -
 850                     TIME2NSEC(lwp->li_usage.pr_wtime))/period;
 851                 lwp->li_vcx = usage->pr_vctx - lwp->li_usage.pr_vctx;
 852                 lwp->li_icx = usage->pr_ictx - lwp->li_usage.pr_ictx;
 853                 lwp->li_scl = usage->pr_sysc - lwp->li_usage.pr_sysc;
 854                 lwp->li_sig = usage->pr_sigs - lwp->li_usage.pr_sigs;
 855                 (void) memcpy(&lwp->li_usage, usage, sizeof (prusage_t));
 856         }
 857 }
 858 
 859 static alias_cache_entry_t *
 860 fetch_zone_alias(const char *zonen)
 861 {
 862         zone_dochandle_t zch;
 863         alias_cache_entry_t *ace;
 864         struct zone_attrtab attrtab;
 865         char out[4 * sizeof (attrtab.zone_attr_value)];
 866         unsigned outlen = 0;
 867         int err;
 868 
 869         /*
 870          * Get handle to zone configuration:
 871          */
 872         if ((zch = zonecfg_init_handle()) == NULL)
 873                 return (NULL);
 874         if (zonecfg_get_handle(zonen, zch) != Z_OK) {
 875                 zonecfg_fini_handle(zch);
 876                 return (NULL);
 877         }
 878 
 879         /*
 880          * Lookup the 'alias' attribute:
 881          */
 882         bzero(&attrtab, sizeof (attrtab));
 883         (void) strlcpy(attrtab.zone_attr_name, "alias",
 884             sizeof (attrtab.zone_attr_name));
 885         if ((zonecfg_lookup_attr(zch, &attrtab)) != Z_OK) {
 886                 zonecfg_fini_handle(zch);
 887                 return (NULL);
 888         }
 889         zonecfg_fini_handle(zch);
 890 
 891         /*
 892          * Attempt to base64 decode the 'alias' attribute value:
 893          */
 894         if ((err = sasl_decode64(attrtab.zone_attr_value,
 895             strlen(attrtab.zone_attr_value), out, sizeof (out), &outlen)) !=
 896             SASL_OK) {
 897                 if (err == SASL_BADPROT) {
 898                         /*
 899                          * If it was invalid base64, hand the raw value on:
 900                          */
 901                         (void) strlcpy(out, attrtab.zone_attr_value,
 902                             sizeof (out));
 903                 } else {
 904                         /*
 905                          * If we failed for any other reason, error out:
 906                          */
 907                         return (NULL);
 908                 }
 909         }
 910 
 911         /*
 912          * Store it in the cache:
 913          */
 914         if ((ace = calloc(1, sizeof (*ace))) == NULL) {
 915                 return (NULL);
 916         }
 917         if ((ace->ace_zonename = strdup(zonen)) == NULL) {
 918                 free(ace);
 919                 return (NULL);
 920         }
 921         if ((ace->ace_alias = strdup(out)) == NULL) {
 922                 free(ace->ace_zonename);
 923                 free(ace);
 924                 return (NULL);
 925         }
 926 
 927         SLIST_INSERT_HEAD(&alias_cache, ace, ace_linkage);
 928 
 929         return (ace);
 930 }
 931 
 932 static const char *
 933 lookup_zone_alias(const char *zonen)
 934 {
 935         alias_cache_entry_t *ace;
 936 
 937         if (enable_alias_cache != TRUE)
 938                 return (zonen);
 939 
 940         /*
 941          * Check if we have a value cached:
 942          */
 943         SLIST_FOREACH(ace, &alias_cache, ace_linkage) {
 944                 if (strcmp(ace->ace_zonename, zonen) == 0)
 945                         return (ace->ace_alias);
 946         }
 947 
 948         /*
 949          * If not, try and fetch it from libzonecfg:
 950          */
 951         if ((ace = fetch_zone_alias(zonen)) != NULL)
 952                 return (ace->ace_alias);
 953 
 954         return (zonen);
 955 }
 956 
 957 static int
 958 read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize)
 959 {
 960         char procfile[MAX_PROCFS_PATH];
 961 
 962         (void) snprintf(procfile, MAX_PROCFS_PATH,
 963             "/proc/%s/%s", pidstr, file);
 964         if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL)
 965                 return (1);
 966         if (pread(fd_getfd(*fd), buf, bufsize, 0) != bufsize) {
 967                 fd_close(*fd);
 968                 return (1);
 969         }
 970         return (0);
 971 }
 972 
 973 static void
 974 add_proc(psinfo_t *psinfo)
 975 {
 976         lwp_info_t *lwp;
 977         id_t lwpid;
 978         pid_t pid = psinfo->pr_pid;
 979 
 980         lwpid = psinfo->pr_lwp.pr_lwpid;
 981         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 982                 lwp = list_add_lwp(&lwps, pid, lwpid);
 983         lwp->li_flags |= LWP_ALIVE | LWP_REPRESENT;
 984         (void) memcpy(&lwp->li_info, psinfo, sizeof (psinfo_t));
 985         lwp->li_info.pr_lwp.pr_pctcpu = lwp->li_info.pr_pctcpu;
 986 }
 987 
 988 static void
 989 add_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, int flags)
 990 {
 991         lwp_info_t *lwp;
 992         pid_t pid = psinfo->pr_pid;
 993         id_t lwpid = lwpsinfo->pr_lwpid;
 994 
 995         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
 996                 lwp = list_add_lwp(&lwps, pid, lwpid);
 997         lwp->li_flags &= ~LWP_REPRESENT;
 998         lwp->li_flags |= LWP_ALIVE;
 999         lwp->li_flags |= flags;
1000         (void) memcpy(&lwp->li_info, psinfo,
1001             sizeof (psinfo_t) - sizeof (lwpsinfo_t));
1002         (void) memcpy(&lwp->li_info.pr_lwp, lwpsinfo, sizeof (lwpsinfo_t));
1003 }
1004 
1005 static void
1006 prstat_scandir(DIR *procdir)
1007 {
1008         char *pidstr;
1009         pid_t pid;
1010         id_t lwpid;
1011         size_t entsz;
1012         long nlwps, nent, i;
1013         char *buf, *ptr;
1014 
1015         fds_t *fds;
1016         lwp_info_t *lwp;
1017         dirent_t *direntp;
1018 
1019         prheader_t      header;
1020         psinfo_t        psinfo;
1021         prusage_t       usage;
1022         lwpsinfo_t      *lwpsinfo;
1023         prusage_t       *lwpusage;
1024 
1025         total_procs = 0;
1026         total_lwps = 0;
1027         total_cpu = 0;
1028         total_mem = 0;
1029 
1030         convert_zone(&zone_tbl);
1031         for (rewinddir(procdir); (direntp = readdir(procdir)); ) {
1032                 pidstr = direntp->d_name;
1033                 if (pidstr[0] == '.')   /* skip "." and ".."  */
1034                         continue;
1035                 pid = atoi(pidstr);
1036                 if (pid == 0 || pid == 2 || pid == 3)
1037                         continue;       /* skip sched, pageout and fsflush */
1038                 if (has_element(&pid_tbl, pid) == 0)
1039                         continue;       /* check if we really want this pid */
1040                 fds = fds_get(pid);     /* get ptr to file descriptors */
1041 
1042                 if (read_procfile(&fds->fds_psinfo, pidstr,
1043                     "psinfo", &psinfo, sizeof (psinfo_t)) != 0)
1044                         continue;
1045                 if (!has_uid(&ruid_tbl, psinfo.pr_uid) ||
1046                     !has_uid(&euid_tbl, psinfo.pr_euid) ||
1047                     !has_element(&prj_tbl, psinfo.pr_projid) ||
1048                     !has_element(&tsk_tbl, psinfo.pr_taskid) ||
1049                     !has_zone(&zone_tbl, psinfo.pr_zoneid)) {
1050                         fd_close(fds->fds_psinfo);
1051                         continue;
1052                 }
1053                 nlwps = psinfo.pr_nlwp + psinfo.pr_nzomb;
1054 
1055                 if (nlwps > 1 && (opts.o_outpmode & (OPT_LWPS | OPT_PSETS))) {
1056                         int rep_lwp = 0;
1057 
1058                         if (read_procfile(&fds->fds_lpsinfo, pidstr, "lpsinfo",
1059                             &header, sizeof (prheader_t)) != 0) {
1060                                 fd_close(fds->fds_psinfo);
1061                                 continue;
1062                         }
1063 
1064                         nent = header.pr_nent;
1065                         entsz = header.pr_entsize * nent;
1066                         ptr = buf = Malloc(entsz);
1067                         if (pread(fd_getfd(fds->fds_lpsinfo), buf,
1068                             entsz, sizeof (struct prheader)) != entsz) {
1069                                 fd_close(fds->fds_lpsinfo);
1070                                 fd_close(fds->fds_psinfo);
1071                                 free(buf);
1072                                 continue;
1073                         }
1074 
1075                         nlwps = 0;
1076                         for (i = 0; i < nent; i++, ptr += header.pr_entsize) {
1077                                 /*LINTED ALIGNMENT*/
1078                                 lwpsinfo = (lwpsinfo_t *)ptr;
1079                                 if (!has_element(&cpu_tbl,
1080                                     lwpsinfo->pr_onpro) ||
1081                                     !has_element(&set_tbl,
1082                                     lwpsinfo->pr_bindpset) ||
1083                                     !has_element(&lgr_tbl, lwpsinfo->pr_lgrp))
1084                                         continue;
1085                                 nlwps++;
1086                                 if ((opts.o_outpmode & (OPT_PSETS | OPT_LWPS))
1087                                     == OPT_PSETS) {
1088                                         /*
1089                                          * If one of process's LWPs is bound
1090                                          * to a given processor set, report the
1091                                          * whole process.  We may be doing this
1092                                          * a few times but we'll get an accurate
1093                                          * lwp count in return.
1094                                          */
1095                                         add_proc(&psinfo);
1096                                 } else {
1097                                         if (rep_lwp == 0) {
1098                                                 rep_lwp = 1;
1099                                                 add_lwp(&psinfo, lwpsinfo,
1100                                                     LWP_REPRESENT);
1101                                         } else {
1102                                                 add_lwp(&psinfo, lwpsinfo, 0);
1103                                         }
1104                                 }
1105                         }
1106                         free(buf);
1107                         if (nlwps == 0) {
1108                                 fd_close(fds->fds_lpsinfo);
1109                                 fd_close(fds->fds_psinfo);
1110                                 continue;
1111                         }
1112                 } else {
1113                         if (!has_element(&cpu_tbl, psinfo.pr_lwp.pr_onpro) ||
1114                             !has_element(&set_tbl, psinfo.pr_lwp.pr_bindpset) ||
1115                             !has_element(&lgr_tbl, psinfo.pr_lwp.pr_lgrp)) {
1116                                 fd_close(fds->fds_psinfo);
1117                                 continue;
1118                         }
1119                         add_proc(&psinfo);
1120                 }
1121                 if (!(opts.o_outpmode & OPT_MSACCT)) {
1122                         total_procs++;
1123                         total_lwps += nlwps;
1124                         continue;
1125                 }
1126                 /*
1127                  * Get more information about processes from /proc/pid/usage.
1128                  * If process has more than one lwp, then we may have to
1129                  * also look at the /proc/pid/lusage file.
1130                  */
1131                 if ((opts.o_outpmode & OPT_LWPS) && (nlwps > 1)) {
1132                         if (read_procfile(&fds->fds_lusage, pidstr, "lusage",
1133                             &header, sizeof (prheader_t)) != 0) {
1134                                 fd_close(fds->fds_lpsinfo);
1135                                 fd_close(fds->fds_psinfo);
1136                                 continue;
1137                         }
1138                         nent = header.pr_nent;
1139                         entsz = header.pr_entsize * nent;
1140                         buf = Malloc(entsz);
1141                         if (pread(fd_getfd(fds->fds_lusage), buf,
1142                             entsz, sizeof (struct prheader)) != entsz) {
1143                                 fd_close(fds->fds_lusage);
1144                                 fd_close(fds->fds_lpsinfo);
1145                                 fd_close(fds->fds_psinfo);
1146                                 free(buf);
1147                                 continue;
1148                         }
1149                         for (i = 1, ptr = buf + header.pr_entsize; i < nent;
1150                             i++, ptr += header.pr_entsize) {
1151                                 /*LINTED ALIGNMENT*/
1152                                 lwpusage = (prusage_t *)ptr;
1153                                 lwpid = lwpusage->pr_lwpid;
1154                                 /*
1155                                  * New LWPs created after we read lpsinfo
1156                                  * will be ignored.  Don't want to do
1157                                  * everything all over again.
1158                                  */
1159                                 if ((lwp = lwpid_get(pid, lwpid)) == NULL)
1160                                         continue;
1161                                 lwp_update(lwp, pid, lwpid, lwpusage);
1162                         }
1163                         free(buf);
1164                 } else {
1165                         if (read_procfile(&fds->fds_usage, pidstr, "usage",
1166                             &usage, sizeof (prusage_t)) != 0) {
1167                                 fd_close(fds->fds_lpsinfo);
1168                                 fd_close(fds->fds_psinfo);
1169                                 continue;
1170                         }
1171                         lwpid = psinfo.pr_lwp.pr_lwpid;
1172                         if ((lwp = lwpid_get(pid, lwpid)) == NULL)
1173                                 continue;
1174                         lwp_update(lwp, pid, lwpid, &usage);
1175                 }
1176                 total_procs++;
1177                 total_lwps += nlwps;
1178         }
1179         fd_update();
1180 }
1181 
1182 /*
1183  * This procedure removes all dead lwps from the linked list of all lwps.
1184  * It also creates linked list of ids if necessary.
1185  */
1186 static void
1187 list_refresh(list_t *list)
1188 {
1189         lwp_info_t *lwp, *lwp_next;
1190 
1191         if (!(list->l_type & LT_LWPS))
1192                 return;
1193 
1194         for (lwp = list->l_head; lwp != NULL; ) {
1195                 if (lwp->li_flags & LWP_ALIVE) {
1196                         /*
1197                          * Process all live LWPs.
1198                          * When we're done, mark them as dead.
1199                          * They will be marked "alive" on the next
1200                          * /proc scan if they still exist.
1201                          */
1202                         lwp->li_key = list_getkeyval(list, lwp);
1203                         if (opts.o_outpmode & OPT_USERS)
1204                                 list_update(&users, lwp);
1205                         if (opts.o_outpmode & OPT_TASKS)
1206                                 list_update(&tasks, lwp);
1207                         if (opts.o_outpmode & OPT_PROJECTS)
1208                                 list_update(&projects, lwp);
1209                         if (opts.o_outpmode & OPT_ZONES)
1210                                 list_update(&zones, lwp);
1211                         if (opts.o_outpmode & OPT_LGRP)
1212                                 list_update(&lgroups, lwp);
1213                         lwp->li_flags &= ~LWP_ALIVE;
1214                         lwp = lwp->li_next;
1215 
1216                 } else {
1217                         lwp_next = lwp->li_next;
1218                         list_remove_lwp(&lwps, lwp);
1219                         lwp = lwp_next;
1220                 }
1221         }
1222 }
1223 
1224 static void
1225 curses_on()
1226 {
1227         if ((opts.o_outpmode & OPT_TERMCAP) && (is_curses_on == FALSE)) {
1228                 (void) initscr();
1229                 (void) nonl();
1230                 (void) putp(t_smcup);
1231                 is_curses_on = TRUE;
1232         }
1233 }
1234 
1235 static void
1236 curses_off()
1237 {
1238         if ((is_curses_on == TRUE) && (opts.o_outpmode & OPT_TERMCAP)) {
1239                 (void) putp(t_rmcup);
1240                 (void) endwin();
1241                 is_curses_on = FALSE;
1242         }
1243         (void) fflush(stdout);
1244 }
1245 
1246 static int
1247 nlines()
1248 {
1249         struct winsize ws;
1250         char *envp;
1251         int n;
1252         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
1253                 if (ws.ws_row > 0)
1254                         return (ws.ws_row);
1255         }
1256         if (envp = getenv("LINES")) {
1257                 if ((n = Atoi(envp)) > 0) {
1258                         opts.o_outpmode &= ~OPT_USEHOME;
1259                         return (n);
1260                 }
1261         }
1262         return (-1);
1263 }
1264 
1265 static void
1266 setmovecur()
1267 {
1268         int i, n;
1269         if ((opts.o_outpmode & OPT_FULLSCREEN) &&
1270             (opts.o_outpmode & OPT_USEHOME)) {
1271                 movecur = t_home;
1272                 return;
1273         }
1274         if (opts.o_outpmode & OPT_SPLIT) {
1275                 if (opts.o_ntop == 0)
1276                         n = opts.o_nbottom + 1;
1277                 else
1278                         n = opts.o_ntop + opts.o_nbottom + 2;
1279         } else {
1280                 if (opts.o_outpmode & OPT_USERS)
1281                         n = opts.o_nbottom + 1;
1282                 else
1283                         n = opts.o_ntop + 1;
1284         }
1285         if (((opts.o_outpmode & OPT_UDATE) || (opts.o_outpmode & OPT_DDATE)))
1286                 n++;
1287 
1288         if (movecur != NULL && movecur != empty_string && movecur != t_home)
1289                 free(movecur);
1290         movecur = Zalloc(strlen(t_up) * (n + 5));
1291         for (i = 0; i <= n; i++)
1292                 (void) strcat(movecur, t_up);
1293 }
1294 
1295 static int
1296 setsize()
1297 {
1298         static int oldn = 0;
1299         int n;
1300 
1301         if (opts.o_outpmode & OPT_FULLSCREEN) {
1302                 n = nlines();
1303                 if (n == oldn)
1304                         return (0);
1305                 oldn = n;
1306                 if (n == -1) {
1307                         opts.o_outpmode &= ~OPT_USEHOME;
1308                         setmovecur();           /* set default window size */
1309                         return (1);
1310                 }
1311                 n = n - 3;      /* minus header, total and cursor lines */
1312                 if ((opts.o_outpmode & OPT_UDATE) ||
1313                     (opts.o_outpmode & OPT_DDATE))
1314                         n--;    /* minus timestamp */
1315                 if (n < 1)
1316                         Die(gettext("window is too small (try -n)\n"));
1317                 if (opts.o_outpmode & OPT_SPLIT) {
1318                         if (n < 8) {
1319                                 Die(gettext("window is too small (try -n)\n"));
1320                         } else {
1321                                 opts.o_ntop = (n / 4) * 3;
1322                                 opts.o_nbottom = n - 1 - opts.o_ntop;
1323                         }
1324                 } else {
1325                         if (opts.o_outpmode & OPT_USERS)
1326                                 opts.o_nbottom = n;
1327                         else
1328                                 opts.o_ntop = n;
1329                 }
1330         }
1331         setmovecur();
1332         return (1);
1333 }
1334 
1335 static void
1336 ldtermcap()
1337 {
1338         int err;
1339         if (setupterm(NULL, STDIN_FILENO, &err) == ERR) {
1340                 switch (err) {
1341                 case 0:
1342                         Warn(gettext("failed to load terminal info, "
1343                             "defaulting to -c option\n"));
1344                         break;
1345                 case -1:
1346                         Warn(gettext("terminfo database not found, "
1347                             "defaulting to -c option\n"));
1348                         break;
1349                 default:
1350                         Warn(gettext("failed to initialize terminal, "
1351                             "defaulting to -c option\n"));
1352                 }
1353                 opts.o_outpmode &= ~OPT_TERMCAP;
1354                 t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
1355                 t_ulon = t_uloff = empty_string;
1356                 return;
1357         }
1358         t_ulon  = tigetstr("smul");
1359         t_uloff = tigetstr("rmul");
1360         t_up    = tigetstr("cuu1");
1361         t_eol   = tigetstr("el");
1362         t_smcup = tigetstr("smcup");
1363         t_rmcup = tigetstr("rmcup");
1364         t_home  = tigetstr("home");
1365         if ((t_up == (char *)-1) || (t_eol == (char *)-1) ||
1366             (t_smcup == (char *)-1) || (t_rmcup == (char *)-1)) {
1367                 opts.o_outpmode &= ~OPT_TERMCAP;
1368                 t_up = t_eol = t_smcup = t_rmcup = movecur = empty_string;
1369                 return;
1370         }
1371         if (t_up == NULL || t_eol == NULL) {
1372                 opts.o_outpmode &= ~OPT_TERMCAP;
1373                 t_eol = t_up = movecur = empty_string;
1374                 return;
1375         }
1376         if (t_ulon == (char *)-1 || t_uloff == (char *)-1 ||
1377             t_ulon == NULL || t_uloff == NULL) {
1378                 t_ulon = t_uloff = empty_string;  /* can live without it */
1379         }
1380         if (t_smcup == NULL || t_rmcup == NULL)
1381                 t_smcup = t_rmcup = empty_string;
1382         if (t_home == (char *)-1 || t_home == NULL) {
1383                 opts.o_outpmode &= ~OPT_USEHOME;
1384                 t_home = empty_string;
1385         }
1386 }
1387 
1388 static void
1389 sig_handler(int sig)
1390 {
1391         switch (sig) {
1392         case SIGTSTP:   sigtstp = 1;
1393                         break;
1394         case SIGWINCH:  sigwinch = 1;
1395                         break;
1396         case SIGINT:
1397         case SIGTERM:   sigterm = 1;
1398                         break;
1399         }
1400 }
1401 
1402 static void
1403 set_signals()
1404 {
1405         (void) signal(SIGTSTP, sig_handler);
1406         (void) signal(SIGINT, sig_handler);
1407         (void) signal(SIGTERM, sig_handler);
1408         if (opts.o_outpmode & OPT_FULLSCREEN)
1409                 (void) signal(SIGWINCH, sig_handler);
1410 }
1411 
1412 static void
1413 fill_table(table_t *table, char *arg, char option)
1414 {
1415         char *p = strtok(arg, ", ");
1416 
1417         if (p == NULL)
1418                 Die(gettext("invalid argument for -%c\n"), option);
1419 
1420         add_element(table, (long)Atoi(p));
1421         while (p = strtok(NULL, ", "))
1422                 add_element(table, (long)Atoi(p));
1423 }
1424 
1425 static void
1426 fill_prj_table(char *arg)
1427 {
1428         projid_t projid;
1429         char *p = strtok(arg, ", ");
1430 
1431         if (p == NULL)
1432                 Die(gettext("invalid argument for -j\n"));
1433 
1434         if ((projid = getprojidbyname(p)) == -1)
1435                 projid = Atoi(p);
1436         add_element(&prj_tbl, (long)projid);
1437 
1438         while (p = strtok(NULL, ", ")) {
1439                 if ((projid = getprojidbyname(p)) == -1)
1440                         projid = Atoi(p);
1441                 add_element(&prj_tbl, (long)projid);
1442         }
1443 }
1444 
1445 static void
1446 fill_set_table(char *arg)
1447 {
1448         char *p = strtok(arg, ", ");
1449         psetid_t id;
1450 
1451         if (p == NULL)
1452                 Die(gettext("invalid argument for -C\n"));
1453 
1454         if ((id = Atoi(p)) == 0)
1455                 id = PS_NONE;
1456         add_element(&set_tbl, id);
1457         while (p = strtok(NULL, ", ")) {
1458                 if ((id = Atoi(p)) == 0)
1459                         id = PS_NONE;
1460                 if (!has_element(&set_tbl, id))
1461                         add_element(&set_tbl, id);
1462         }
1463 }
1464 
1465 static void
1466 Exit()
1467 {
1468         curses_off();
1469         list_clear(&lwps);
1470         list_clear(&users);
1471         list_clear(&tasks);
1472         list_clear(&projects);
1473         list_clear(&zones);
1474         fd_exit();
1475 }
1476 
1477 
1478 int
1479 main(int argc, char **argv)
1480 {
1481         DIR *procdir;
1482         char *p;
1483         char *sortk = "cpu";    /* default sort key */
1484         int opt;
1485         int timeout;
1486         struct pollfd pollset;
1487         char key;
1488 
1489         (void) setlocale(LC_ALL, "");
1490         (void) textdomain(TEXT_DOMAIN);
1491         Progname(argv[0]);
1492         lwpid_init();
1493         fd_init(Setrlimit());
1494 
1495         pagesize = sysconf(_SC_PAGESIZE);
1496 
1497         while ((opt = getopt(argc, argv,
1498             "vVcd:HmAarRLtu:U:n:p:C:P:h:s:S:j:k:TJz:Z")) != (int)EOF) {
1499                 switch (opt) {
1500                 case 'A':
1501                         enable_alias_cache = TRUE;
1502                         break;
1503                 case 'r':
1504                         opts.o_outpmode |= OPT_NORESOLVE;
1505                         break;
1506                 case 'R':
1507                         opts.o_outpmode |= OPT_REALTIME;
1508                         break;
1509                 case 'c':
1510                         opts.o_outpmode &= ~OPT_TERMCAP;
1511                         opts.o_outpmode &= ~OPT_FULLSCREEN;
1512                         break;
1513                 case 'd':
1514                         if (optarg) {
1515                                 if (*optarg == 'u')
1516                                         opts.o_outpmode |= OPT_UDATE;
1517                                 else if (*optarg == 'd')
1518                                         opts.o_outpmode |= OPT_DDATE;
1519                                 else
1520                                         Usage();
1521                         } else {
1522                                 Usage();
1523                         }
1524                         break;
1525                 case 'h':
1526                         fill_table(&lgr_tbl, optarg, 'h');
1527                         break;
1528                 case 'H':
1529                         opts.o_outpmode |= OPT_LGRP;
1530                         break;
1531                 case 'm':
1532                 case 'v':
1533                         opts.o_outpmode &= ~OPT_PSINFO;
1534                         opts.o_outpmode |=  OPT_MSACCT;
1535                         break;
1536                 case 't':
1537                         opts.o_outpmode &= ~OPT_PSINFO;
1538                         opts.o_outpmode |= OPT_USERS;
1539                         break;
1540                 case 'a':
1541                         opts.o_outpmode |= OPT_SPLIT | OPT_USERS;
1542                         break;
1543                 case 'T':
1544                         opts.o_outpmode |= OPT_SPLIT | OPT_TASKS;
1545                         break;
1546                 case 'J':
1547                         opts.o_outpmode |= OPT_SPLIT | OPT_PROJECTS;
1548                         break;
1549                 case 'n':
1550                         if ((p = strtok(optarg, ",")) == NULL)
1551                                 Die(gettext("invalid argument for -n\n"));
1552                         opts.o_ntop = Atoi(p);
1553                         if (p = strtok(NULL, ","))
1554                                 opts.o_nbottom = Atoi(p);
1555                         else if (opts.o_ntop == 0)
1556                                 opts.o_nbottom = 5;
1557                         opts.o_outpmode &= ~OPT_FULLSCREEN;
1558                         break;
1559                 case 's':
1560                         opts.o_sortorder = -1;
1561                         sortk = optarg;
1562                         break;
1563                 case 'S':
1564                         opts.o_sortorder = 1;
1565                         sortk = optarg;
1566                         break;
1567                 case 'u':
1568                         if ((p = strtok(optarg, ", ")) == NULL)
1569                                 Die(gettext("invalid argument for -u\n"));
1570                         add_uid(&euid_tbl, p);
1571                         while (p = strtok(NULL, ", "))
1572                                 add_uid(&euid_tbl, p);
1573                         break;
1574                 case 'U':
1575                         if ((p = strtok(optarg, ", ")) == NULL)
1576                                 Die(gettext("invalid argument for -U\n"));
1577                         add_uid(&ruid_tbl, p);
1578                         while (p = strtok(NULL, ", "))
1579                                 add_uid(&ruid_tbl, p);
1580                         break;
1581                 case 'V':
1582                         opts.o_outpmode |= OPT_VMUSAGE;
1583                         break;
1584                 case 'p':
1585                         fill_table(&pid_tbl, optarg, 'p');
1586                         break;
1587                 case 'C':
1588                         fill_set_table(optarg);
1589                         opts.o_outpmode |= OPT_PSETS;
1590                         break;
1591                 case 'P':
1592                         fill_table(&cpu_tbl, optarg, 'P');
1593                         break;
1594                 case 'k':
1595                         fill_table(&tsk_tbl, optarg, 'k');
1596                         break;
1597                 case 'j':
1598                         fill_prj_table(optarg);
1599                         break;
1600                 case 'L':
1601                         opts.o_outpmode |= OPT_LWPS;
1602                         break;
1603                 case 'z':
1604                         if ((p = strtok(optarg, ", ")) == NULL)
1605                                 Die(gettext("invalid argument for -z\n"));
1606                         add_zone(&zone_tbl, p);
1607                         while (p = strtok(NULL, ", "))
1608                                 add_zone(&zone_tbl, p);
1609                         break;
1610                 case 'Z':
1611                         opts.o_outpmode |= OPT_SPLIT | OPT_ZONES;
1612                         break;
1613                 default:
1614                         Usage();
1615                 }
1616         }
1617 
1618         (void) atexit(Exit);
1619         if ((opts.o_outpmode & OPT_USERS) &&
1620             !(opts.o_outpmode & OPT_SPLIT))
1621                 opts.o_nbottom = opts.o_ntop;
1622         if (!(opts.o_outpmode & OPT_SPLIT) && opts.o_ntop == 0)
1623                 Die(gettext("invalid argument for -n\n"));
1624         if (opts.o_nbottom == 0)
1625                 Die(gettext("invalid argument for -n\n"));
1626         if (!(opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode & OPT_USERS) &&
1627             ((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
1628                 Die(gettext("-t option cannot be used with -v or -m\n"));
1629 
1630         if ((opts.o_outpmode & OPT_SPLIT) && (opts.o_outpmode & OPT_USERS) &&
1631             !((opts.o_outpmode & (OPT_PSINFO | OPT_MSACCT))))
1632                 Die(gettext("-t option cannot be used with "
1633                     "-a, -J, -T or -Z\n"));
1634 
1635         if ((opts.o_outpmode & OPT_USERS) &&
1636             (opts.o_outpmode & (OPT_TASKS | OPT_PROJECTS | OPT_ZONES)))
1637                 Die(gettext("-a option cannot be used with "
1638                     "-t, -J, -T or -Z\n"));
1639 
1640         if (((opts.o_outpmode & OPT_TASKS) &&
1641             (opts.o_outpmode & (OPT_PROJECTS|OPT_ZONES))) ||
1642             ((opts.o_outpmode & OPT_PROJECTS) &&
1643             (opts.o_outpmode & (OPT_TASKS|OPT_ZONES)))) {
1644                 Die(gettext(
1645                     "-J, -T and -Z options are mutually exclusive\n"));
1646         }
1647 
1648         /*
1649          * There is not enough space to combine microstate information and
1650          * lgroup information and still fit in 80-column output.
1651          */
1652         if ((opts.o_outpmode & OPT_LGRP) && (opts.o_outpmode & OPT_MSACCT)) {
1653                 Die(gettext("-H and -m options are mutually exclusive\n"));
1654         }
1655 
1656         if (argc > optind)
1657                 opts.o_interval = Atoi(argv[optind++]);
1658         if (argc > optind)
1659                 opts.o_count = Atoi(argv[optind++]);
1660         if (opts.o_count == 0)
1661                 Die(gettext("invalid counter value\n"));
1662         if (argc > optind)
1663                 Usage();
1664         if (opts.o_outpmode & OPT_REALTIME)
1665                 Priocntl("RT");
1666         if (isatty(STDOUT_FILENO) == 1 && isatty(STDIN_FILENO))
1667                 opts.o_outpmode |= OPT_TTY;     /* interactive */
1668         if (!(opts.o_outpmode & OPT_TTY)) {
1669                 opts.o_outpmode &= ~OPT_TERMCAP; /* no termcap for pipes */
1670                 opts.o_outpmode &= ~OPT_FULLSCREEN;
1671         }
1672         if (opts.o_outpmode & OPT_TERMCAP)
1673                 ldtermcap();            /* can turn OPT_TERMCAP off */
1674         if (opts.o_outpmode & OPT_TERMCAP)
1675                 (void) setsize();
1676         list_alloc(&lwps, opts.o_ntop);
1677         list_alloc(&users, opts.o_nbottom);
1678         list_alloc(&tasks, opts.o_nbottom);
1679         list_alloc(&projects, opts.o_nbottom);
1680         list_alloc(&zones, opts.o_nbottom);
1681         list_alloc(&lgroups, opts.o_nbottom);
1682         list_setkeyfunc(sortk, &opts, &lwps, LT_LWPS);
1683         list_setkeyfunc(NULL, &opts, &users, LT_USERS);
1684         list_setkeyfunc(NULL, &opts, &tasks, LT_TASKS);
1685         list_setkeyfunc(NULL, &opts, &projects, LT_PROJECTS);
1686         list_setkeyfunc(NULL, &opts, &zones, LT_ZONES);
1687         list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS);
1688         if (opts.o_outpmode & OPT_TERMCAP)
1689                 curses_on();
1690         if ((procdir = opendir("/proc")) == NULL)
1691                 Die(gettext("cannot open /proc directory\n"));
1692         if (opts.o_outpmode & OPT_TTY) {
1693                 (void) printf(gettext("Please wait...\r"));
1694                 if (!(opts.o_outpmode & OPT_TERMCAP))
1695                         (void) putchar('\n');
1696                 (void) fflush(stdout);
1697         }
1698         set_signals();
1699         pollset.fd = STDIN_FILENO;
1700         pollset.events = POLLIN;
1701         timeout = opts.o_interval * MILLISEC;
1702 
1703         /*
1704          * main program loop
1705          */
1706         do {
1707                 if (sigterm == 1)
1708                         break;
1709                 if (sigtstp == 1) {
1710                         curses_off();
1711                         (void) signal(SIGTSTP, SIG_DFL);
1712                         (void) kill(0, SIGTSTP);
1713                         /*
1714                          * prstat stops here until it receives SIGCONT signal.
1715                          */
1716                         sigtstp = 0;
1717                         (void) signal(SIGTSTP, sig_handler);
1718                         curses_on();
1719                         print_movecur = FALSE;
1720                         if (opts.o_outpmode & OPT_FULLSCREEN)
1721                                 sigwinch = 1;
1722                 }
1723                 if (sigwinch == 1) {
1724                         if (setsize() == 1) {
1725                                 list_free(&lwps);
1726                                 list_free(&users);
1727                                 list_free(&tasks);
1728                                 list_free(&projects);
1729                                 list_free(&zones);
1730                                 list_alloc(&lwps, opts.o_ntop);
1731                                 list_alloc(&users, opts.o_nbottom);
1732                                 list_alloc(&tasks, opts.o_nbottom);
1733                                 list_alloc(&projects, opts.o_nbottom);
1734                                 list_alloc(&zones, opts.o_nbottom);
1735                         }
1736                         sigwinch = 0;
1737                         (void) signal(SIGWINCH, sig_handler);
1738                 }
1739                 prstat_scandir(procdir);
1740                 list_refresh(&lwps);
1741                 if (print_movecur)
1742                         (void) putp(movecur);
1743                 print_movecur = TRUE;
1744                 if ((opts.o_outpmode & OPT_PSINFO) ||
1745                     (opts.o_outpmode & OPT_MSACCT)) {
1746                         list_sort(&lwps);
1747                         list_print(&lwps);
1748                 }
1749                 if (opts.o_outpmode & OPT_USERS) {
1750                         list_getsize(&users);
1751                         list_sort(&users);
1752                         list_print(&users);
1753                         list_clear(&users);
1754                 }
1755                 if (opts.o_outpmode & OPT_TASKS) {
1756                         list_getsize(&tasks);
1757                         list_sort(&tasks);
1758                         list_print(&tasks);
1759                         list_clear(&tasks);
1760                 }
1761                 if (opts.o_outpmode & OPT_PROJECTS) {
1762                         list_getsize(&projects);
1763                         list_sort(&projects);
1764                         list_print(&projects);
1765                         list_clear(&projects);
1766                 }
1767                 if (opts.o_outpmode & OPT_ZONES) {
1768                         list_getsize(&zones);
1769                         list_sort(&zones);
1770                         list_print(&zones);
1771                         list_clear(&zones);
1772                 }
1773                 if (opts.o_count == 1)
1774                         break;
1775                 /*
1776                  * If poll() returns -1 and sets errno to EINTR here because
1777                  * the process received a signal, it is Ok to abort this
1778                  * timeout and loop around because we check the signals at the
1779                  * top of the loop.
1780                  */
1781                 if (opts.o_outpmode & OPT_TTY) {
1782                         if (poll(&pollset, (nfds_t)1, timeout) > 0) {
1783                                 if (read(STDIN_FILENO, &key, 1) == 1) {
1784                                         if (tolower(key) == 'q')
1785                                                 break;
1786                                 }
1787                         }
1788                 } else {
1789                         (void) sleep(opts.o_interval);
1790                 }
1791         } while (opts.o_count == (-1) || --opts.o_count);
1792 
1793         if (opts.o_outpmode & OPT_TTY)
1794                 (void) putchar('\r');
1795         return (0);
1796 }