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