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