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