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