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
134static char	*t_ulon;			/* termcap: start underline */
135static char	*t_uloff;			/* termcap: end underline */
136static char	*t_up;				/* termcap: cursor 1 line up */
137static char	*t_eol;				/* termcap: clear end of line */
138static char	*t_smcup;			/* termcap: cursor mvcap on */
139static char	*t_rmcup;			/* termcap: cursor mvcap off */
140static char	*t_home;			/* termcap: move cursor home */
141static char	*movecur = NULL;		/* termcap: move up string */
142static char	*empty_string = "\0";		/* termcap: empty string */
143static uint_t	print_movecur = FALSE;		/* print movecur or not */
144static int	is_curses_on = FALSE;		/* current curses state */
145
146static table_t	pid_tbl = {0, 0, NULL};		/* selected processes */
147static table_t	cpu_tbl = {0, 0, NULL};		/* selected processors */
148static table_t  set_tbl = {0, 0, NULL};		/* selected processor sets */
149static table_t	prj_tbl = {0, 0, NULL};		/* selected projects */
150static table_t	tsk_tbl = {0, 0, NULL};		/* selected tasks */
151static table_t	lgr_tbl = {0, 0, NULL};		/* selected lgroups */
152static zonetbl_t zone_tbl = {0, 0, NULL};	/* selected zones */
153static uidtbl_t euid_tbl = {0, 0, NULL};	/* selected effective users */
154static uidtbl_t ruid_tbl = {0, 0, NULL};	/* selected real users */
155
156static uint_t	total_procs;			/* total number of procs */
157static uint_t	total_lwps;			/* total number of lwps */
158static float	total_cpu;			/* total cpu usage */
159static float	total_mem;			/* total memory usage */
160
161static list_t	lwps;				/* list of lwps/processes */
162static list_t	users;				/* list of users */
163static list_t	tasks;				/* list of tasks */
164static list_t	projects;			/* list of projects */
165static list_t	zones;				/* list of zones */
166static list_t	lgroups;			/* list of lgroups */
167
168static volatile uint_t sigwinch = 0;
169static volatile uint_t sigtstp = 0;
170static volatile uint_t sigterm = 0;
171
172static long pagesize;
173
174/* default settings */
175
176optdesc_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 */
189static void
190print_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
213static void
214psetloadavg(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 */
230static void
231list_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 */
354static void
355list_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
625static lwp_info_t *
626list_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
645static void
646list_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
663static void
664list_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
690static void
691list_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;
744update:
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
768static void
769lwp_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
849static int
850read_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
865static void
866add_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
880static void
881get_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
901static void
902add_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
919static void
920prstat_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 */
1100static void
1101list_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
1138static void
1139curses_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
1149static void
1150curses_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
1160static int
1161nlines(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
1193static void
1194setmovecur(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
1223static int
1224setsize(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
1265static void
1266ldtermcap()
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
1318static void
1319sig_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
1332static void
1333set_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
1342static void
1343fill_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
1355static void
1356fill_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
1375static void
1376fill_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
1395static void
1396Exit()
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
1408int
1409main(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