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