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