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