Home | History | Annotate | Download | only in prstat
      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 }
   1656