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