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