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