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