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