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