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