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