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