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