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