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