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