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