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