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