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