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