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