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