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