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