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