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