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