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