1/* top.c - Source file: show Linux processes */ 2/* 3 * Copyright (c) 2002, by: James C. Warner 4 * All rights reserved. 8921 Hilloway Road 5 * Eden Prairie, Minnesota 55347 USA 6 * <warnerjc@worldnet.att.net> 7 * 8 * This file may be used subject to the terms and conditions of the 9 * GNU Library General Public License Version 2, or any later version 10 * at your option, as published by the Free Software Foundation. 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Library General Public License for more details. 15 * 16 * For their contributions to this program, the author wishes to thank: 17 * Albert D. Cahalan, <albert@users.sf.net> 18 * Craig Small, <csmall@small.dropbear.id.au> 19 * 20 * Changes by Albert Cahalan, 2002. 21 */ 22#include <sys/ioctl.h> 23#include <sys/resource.h> 24#include <sys/time.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <ctype.h> 28#include <curses.h> 29#include <errno.h> 30#include <fcntl.h> 31#include <signal.h> 32#include <stdarg.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <term.h> 37#include <termios.h> 38#include <time.h> 39#include <unistd.h> 40#include <values.h> 41 42#include "proc/devname.h" 43#include "proc/wchan.h" 44#include "proc/procps.h" 45#include "proc/readproc.h" 46#include "proc/escape.h" 47#include "proc/sig.h" 48#ifdef USE_LIB_STA3 49#include "proc/status.h" 50#endif 51#include "proc/sysinfo.h" 52#include "proc/version.h" 53#include "proc/whattime.h" 54 55#include "top.h" 56 57/*###### Miscellaneous global stuff ####################################*/ 58 /* Used for recording data to and reading data from a file. */ 59static FILE *outfile; 60static FILE *datafile; 61static int o_flag; 62 /* The original and new terminal attributes */ 63static struct termios Savedtty, Rawtty; 64static int Ttychanged = 0; 65 66 /* Program name used in error messages and local 'rc' file name */ 67static char *Myname; 68 69 /* Name of user config file (dynamically constructed) and our 70 'Current' rcfile contents, initialized with defaults but may be 71 overridden with the local rcfile (old or new-style) values */ 72static char Rc_name[OURPATHSZ]; 73static RCF_t Rc = DEF_RCFILE; 74 75 /* The run-time acquired page size */ 76static int Page_size; 77 78 /* SMP, Irix/Solaris mode, Linux 2.5.xx support */ 79static int Cpu_tot, *Cpu_map; 80 /* assume no IO-wait stats, overridden if linux 2.5.41 */ 81static const char *States_fmts = STATES_line2x4; 82 83 /* Specific process id monitoring support */ 84static pid_t Monpids[MONPIDMAX] = { 0 }; 85 86static int Monpidsidx = 0; 87 88 /* A postponed error message */ 89static char Msg_delayed[SMLBUFSIZ]; 90static int Msg_awaiting = 0; 91 92 /* Configurable Display support ################################## */ 93 94 /* Current screen dimensions. 95 note: the number of processes displayed is tracked on a per window 96 basis (see the WIN_t). Max_lines is the total number of 97 screen rows after deducting summary information overhead. */ 98 /* Current terminal screen size. */ 99static int Screen_cols, Screen_rows, Max_lines; 100 101 /* This is really the number of lines needed to display the summary 102 information (0 - nn), but is used as the relative row where we 103 stick the cursor between frames. */ 104static int Msg_row; 105 106 /* Global/Non-windows mode stuff that is NOT persistent */ 107static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise 108 PSDBopen = 0, // set to '1' if psdb opened (now postponed) 109 Batch = 0, // batch mode, collect no input, dumb output 110 Loops = -1, // number of iterations, -1 loops forever 111 Secure_mode = 0; // set if some functionality restricted 112 113 /* Some cap's stuff to reduce runtime calls -- 114 to accomodate 'Batch' mode, they begin life as empty strings */ 115static char Cap_clr_eol[CAPBUFSIZ] = "", 116 Cap_clr_eos[CAPBUFSIZ] = "", 117 Cap_clr_scr[CAPBUFSIZ] = "", 118 Cap_curs_norm[CAPBUFSIZ] = "", 119 Cap_curs_huge[CAPBUFSIZ] = "", 120 Cap_home[CAPBUFSIZ] = "", 121 Cap_norm[CAPBUFSIZ] = "", 122 Cap_reverse[CAPBUFSIZ] = "", Caps_off[CAPBUFSIZ] = ""; 123static int Cap_can_goto = 0; 124 125 /* Some optimization stuff, to reduce output demands... 126 The Pseudo_ guys are managed by wins_resize and frame_make. They 127 are exploited in a macro and represent 90% of our optimization. 128 The Stdout_buf is transparent to our code and regardless of whose 129 buffer is used, stdout is flushed at frame end or if interactive. */ 130static char *Pseudo_scrn; 131static int Pseudo_row, Pseudo_cols, Pseudo_size; 132#ifndef STDOUT_IOLBF 133 // less than stdout's normal buffer but with luck mostly '\n' anyway 134static char Stdout_buf[2048]; 135#endif 136 137 /* ////////////////////////////////////////////////////////////// */ 138 /* Special Section: multiple windows/field groups --------------- */ 139 140 /* The pointers to our four WIN_t's, and which of those is considered 141 the 'current' window (ie. which window is associated with any summ 142 info displayed and to which window commands are directed) */ 143static WIN_t Winstk[GROUPSMAX], *Curwin; 144 145 /* Frame oriented stuff that can't remain local to any 1 function 146 and/or that would be too cumbersome managed as parms, 147 and/or that are simply more efficiently handled as globals 148 (first 2 persist beyond a single frame, changed infrequently) */ 149static int Frames_libflags; // PROC_FILLxxx flags (0 = need new) 150//atic int Frames_maxcmdln; // the largest from the 4 windows 151static unsigned Frame_maxtask; // last known number of active tasks 152 // ie. current 'size' of proc table 153static unsigned Frame_running, // state categories for this frame 154 Frame_sleepin, Frame_stopped, Frame_zombied; 155static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu' 156static int Frame_srtflg, // the subject window's sort direction 157 Frame_ctimes, // the subject window's ctimes flag 158 Frame_cmdlin; // the subject window's cmdlin flag 159 /* ////////////////////////////////////////////////////////////// */ 160 161/*###### Sort callbacks ################################################*/ 162 163 /* 164 * These happen to be coded in the same order as the enum 'pflag' 165 * values. Note that 2 of these routines serve double duty -- 166 * 2 columns each. 167 */ 168 169SCB_NUMx(P_PID, pid) 170 SCB_NUMx(P_PPD, ppid) 171 SCB_STRx(P_URR, ruser) 172 SCB_NUMx(P_UID, euid) 173 SCB_STRx(P_URE, euser) 174 SCB_STRx(P_GRP, egroup) 175 SCB_NUMx(P_TTY, tty) 176 SCB_NUMx(P_PRI, priority) 177 SCB_NUMx(P_NCE, nice) 178 SCB_NUMx(P_CPN, processor) 179 SCB_NUM1(P_CPU, pcpu) 180 // also serves P_TM2 ! 181static int sort_P_TME(const proc_t ** P, const proc_t ** Q) 182{ 183 if (Frame_ctimes) { 184 if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) 185 < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime)) 186 return SORT_lt; 187 if (((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) 188 > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime)) 189 return SORT_gt; 190 } else { 191 if (((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) 192 return SORT_lt; 193 if (((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) 194 return SORT_gt; 195 } 196 return SORT_eq; 197} 198 199SCB_NUM1(P_VRT, size) 200 SCB_NUM2(P_SWP, size, resident) 201 SCB_NUM1(P_RES, resident) // also serves P_MEM ! 202 SCB_NUM1(P_COD, trs) 203 SCB_NUM1(P_DAT, drs) 204 SCB_NUM1(P_SHR, share) 205 SCB_NUM1(P_FLT, maj_flt) 206 SCB_NUM1(P_DRT, dt) 207 SCB_NUMx(P_STA, state) 208 209static int sort_P_CMD(const proc_t ** P, const proc_t ** Q) 210{ 211 /* if a process doesn't have a cmdline, we'll consider it a kernel thread 212 -- since displayed tasks are given special treatment, we must too */ 213 if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { 214 if (!(*Q)->cmdline) 215 return Frame_srtflg * -1; 216 if (!(*P)->cmdline) 217 return Frame_srtflg; 218 return Frame_srtflg * 219 strncmp((*Q)->cmdline[0], (*P)->cmdline[0], 220 (unsigned)Curwin->maxcmdln); 221 } 222 // this part also handles the compare if both are kernel threads 223 return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd); 224} 225 226SCB_NUM1(P_WCH, wchan) 227 SCB_NUM1(P_FLG, flags) 228 229 /* ///////////////////////////////// special sort for prochlp() ! */ 230static int sort_HST_t(const HST_t * P, const HST_t * Q) 231{ 232 return P->pid - Q->pid; 233} 234 235/*###### Tiny useful routine(s) ########################################*/ 236 237 /* 238 * This routine isolates ALL user INPUT and ensures that we 239 * wont be mixing I/O from stdio and low-level read() requests */ 240static int chin(int ech, char *buf, unsigned cnt) 241{ 242 int rc; 243 244 fflush(stdout); 245 if (!ech) 246 rc = read(STDIN_FILENO, buf, cnt); 247 else { 248 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); 249 rc = read(STDIN_FILENO, buf, cnt); 250 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); 251 } 252 /* may be the beginning of a lengthy escape sequence */ 253 tcflush(STDIN_FILENO, TCIFLUSH); 254 return rc; /* note: we do NOT produce a vaid 'string' */ 255} 256 257 /* 258 * This routine simply formats whatever the caller wants and 259 * returns a pointer to the resulting 'const char' string... */ 260static const char *fmtmk(const char *fmts, ...) 261{ 262 static char buf[BIGBUFSIZ]; /* with help stuff, our buffer */ 263 va_list va; /* requirements exceed 1k */ 264 265 va_start(va, fmts); 266 vsnprintf(buf, sizeof(buf), fmts, va); 267 va_end(va); 268 return (const char *)buf; 269} 270 271 /* 272 * This guy is just our way of avoiding the overhead of the standard 273 * strcat function (should the caller choose to participate) */ 274static inline char *scat(char *restrict dst, const char *restrict src) 275{ 276 while (*dst) 277 dst++; 278 while ((*(dst++) = *(src++))) ; 279 return --dst; 280} 281 282// Trim the rc file lines and any 'open_psdb_message' result which arrives 283// with an inappropriate newline (thanks to 'sysmap_mmap') 284static char *strim_0(char *str) 285{ 286 static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape 287 char *p; 288 289 if ((p = strpbrk(str, ws))) 290 *p = 0; 291 return str; 292} 293 294 /* 295 * This guy just facilitates Batch and protects against dumb ttys 296 * -- we'd 'inline' him but he's only called twice per frame, 297 * yet used in many other locations. */ 298static const char *tg2(int x, int y) 299{ 300 return Cap_can_goto ? tgoto(cursor_address, x, y) : ""; 301} 302 303/*###### Exit/Interrput routines #######################################*/ 304 305 /* 306 * The usual program end -- 307 * called only by functions in this section. */ 308static void bye_bye(int eno, const char *str) NORETURN; 309static void bye_bye(int eno, const char *str) 310{ 311 if (!Batch) 312 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); 313 putp(tg2(0, Screen_rows)); 314 putp(Cap_curs_norm); 315 putp("\n"); 316 fflush(stdout); 317 318#ifdef ATEOJ_REPORT 319 fprintf(stderr, 320 "\nbye_bye's Summary report:" 321 "\n\tProgram" 322 "\n\t Linux version = %u.%u.%u, %s" 323 "\n\t Hertz = %u (%u bytes, %u-bit time)" 324 "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u" 325 "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)" 326 "\n\t Crufty? %s" 327 "\n\tTerminal: %s" 328 "\n\t device = %s, ncurses = v%s" 329 "\n\t max_colors = %d, max_pairs = %d" 330 "\n\t Cap_can_goto = %s" 331 "\n\t Screen_cols = %d, Screen_rows = %d" 332 "\n\t Max_lines = %d, most recent Pseudo_size = %d" 333#ifndef STDOUT_IOLBF 334 "\n\t Stdout_buf = %d, BUFSIZ = %u" 335#endif 336 "\n\tWindows and Curwin->" 337 "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" 338 "\n\t rc.winname = %s, grpname = %s" 339#ifdef CASEUP_HEXES 340 "\n\t rc.winflags = %08X, maxpflgs = %d" 341#else 342 "\n\t rc.winflags = %08x, maxpflgs = %d" 343#endif 344 "\n\t rc.fieldscur = %s" 345 "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d" 346 "\n\t rc.sortindx = %d" 347 "\n", LINUX_VERSION_MAJOR(linux_version_code) 348 , LINUX_VERSION_MINOR(linux_version_code) 349 , LINUX_VERSION_PATCH(linux_version_code) 350 , procps_version, (unsigned)Hertz, sizeof(Hertz), 351 sizeof(Hertz) * 8, Page_size, Cpu_tot, sizeof(proc_t) 352 , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t) 353#ifdef PRETENDNOCAP 354 , "dumb" 355#else 356 , termname() 357#endif 358 , ttyname(STDOUT_FILENO), NCURSES_VERSION, max_colors, 359 max_pairs, Cap_can_goto ? "yes" : "No!", Screen_cols, 360 Screen_rows, Max_lines, Pseudo_size 361#ifndef STDOUT_IOLBF 362 , sizeof(Stdout_buf), (unsigned)BUFSIZ 363#endif 364 , sizeof(WIN_t), GROUPSMAX, Curwin->rc.winname, Curwin->grpname, 365 Curwin->rc.winflags, Curwin->maxpflgs, Curwin->rc.fieldscur, 366 Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln, 367 Curwin->rc.sortindx); 368#endif 369 370 if (str) { 371 if (eno) 372 perror(str); 373 else { 374 fputs(str, stderr); 375 eno = 1; 376 } 377 } 378 exit(eno); 379} 380 381 /* 382 * Normal end of execution. 383 * catches: 384 * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ 385static void end_pgm(int dont_care_sig) NORETURN; 386static void end_pgm(int dont_care_sig) 387{ 388 (void)dont_care_sig; 389 bye_bye(0, NULL); 390} 391 392 /* 393 * Standard error handler to normalize the look of all err o/p */ 394static void std_err(const char *str) NORETURN; 395static void std_err(const char *str) 396{ 397 static char buf[SMLBUFSIZ]; 398 399 fflush(stdout); 400 /* we'll use our own buffer so callers can still use fmtmk() and, yes the 401 leading tab is not the standard convention, but the standard is wrong 402 -- OUR msg won't get lost in screen clutter, like so many others! */ 403 snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); 404 if (!Ttychanged) { 405 fprintf(stderr, "%s\n", buf); 406 exit(1); 407 } 408 /* not to worry, he'll change our exit code to 1 due to 'buf' */ 409 bye_bye(0, buf); 410} 411 412 /* 413 * Suspend ourself. 414 * catches: 415 * SIGTSTP, SIGTTIN and SIGTTOU */ 416static void suspend(int dont_care_sig) 417{ 418 (void)dont_care_sig; 419 /* reset terminal */ 420 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); 421 putp(tg2(0, Screen_rows)); 422 putp(Cap_curs_norm); 423 fflush(stdout); 424 raise(SIGSTOP); 425 /* later, after SIGCONT... */ 426 if (!Batch) 427 tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); 428} 429 430/*###### Misc Color/Display support ####################################*/ 431 432 /* 433 * Make the appropriate caps/color strings and set some 434 * lengths which are used to distinguish twix the displayed 435 * columns and an actual printed row! 436 * note: we avoid the use of background color so as to maximize 437 * compatibility with the user's xterm settings */ 438static void capsmk(WIN_t * q) 439{ 440 /* macro to test if a basic (non-color) capability is valid 441 thanks: Floyd Davidson <floyd@ptialaska.net> */ 442#define tIF(s) s ? s : "" 443 static int capsdone = 0; 444 445 // we must NOT disturb our 'empty' terminfo strings! 446 if (Batch) 447 return; 448 449 // these are the unchangeable puppies, so we only do 'em once 450 if (!capsdone) { 451 strcpy(Cap_clr_eol, tIF(clr_eol)); 452 strcpy(Cap_clr_eos, tIF(clr_eos)); 453 strcpy(Cap_clr_scr, tIF(clear_screen)); 454 strcpy(Cap_curs_huge, tIF(cursor_visible)); 455 strcpy(Cap_curs_norm, tIF(cursor_normal)); 456 strcpy(Cap_home, tIF(cursor_home)); 457 strcpy(Cap_norm, tIF(exit_attribute_mode)); 458 strcpy(Cap_reverse, tIF(enter_reverse_mode)); 459 snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, 460 tIF(orig_pair)); 461 if (tgoto(cursor_address, 1, 1)) 462 Cap_can_goto = 1; 463 capsdone = 1; 464 } 465 /* the key to NO run-time costs for configurable colors -- we spend a 466 little time with the user now setting up our terminfo strings, and 467 the job's done until he/she/it has a change-of-heart */ 468 strcpy(q->cap_bold, 469 CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode)); 470 if (CHKw(q, Show_COLORS) && max_colors > 0) { 471 strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr)); 472 snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s", 473 tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse); 474 snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s", 475 tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold); 476 snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s", 477 tparm(set_a_foreground, q->rc.headclr), Cap_reverse); 478 snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s", 479 Caps_off, tparm(set_a_foreground, q->rc.taskclr)); 480 } else { 481 q->capclr_sum[0] = '\0'; 482 strcpy(q->capclr_msg, Cap_reverse); 483 strcpy(q->capclr_pmt, q->cap_bold); 484 strcpy(q->capclr_hdr, Cap_reverse); 485 strcpy(q->capclr_rownorm, Cap_norm); 486 } 487 // composite(s), so we do 'em outside and after the if 488 snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s", 489 q->capclr_rownorm, CHKw(q, 490 Show_HIBOLD) ? q-> 491 cap_bold : Cap_reverse); 492 q->len_rownorm = strlen(q->capclr_rownorm); 493 q->len_rowhigh = strlen(q->capclr_rowhigh); 494 495#undef tIF 496} 497 498 /* 499 * Show an error, but not right now. 500 * Due to the postponed opening of ksym, using open_psdb_message, 501 * if P_WCH had been selected and the program is restarted, the 502 * message would otherwise be displayed prematurely. 503 * (old top handles that situation with typical inelegance) */ 504static void msg_save(const char *fmts, ...) 505{ 506 char tmp[SMLBUFSIZ]; 507 va_list va; 508 509 va_start(va, fmts); 510 vsnprintf(tmp, sizeof(tmp), fmts, va); 511 va_end(va); 512 /* we'll add some extra attention grabbers to whatever this is */ 513 snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", 514 strim_0(tmp)); 515 Msg_awaiting = 1; 516} 517 518 /* 519 * Show an error message (caller may include a '\a' for sound) */ 520static void show_msg(const char *str) 521{ 522 PUTT("%s%s %s %s%s", tg2(0, Msg_row) 523 , Curwin->capclr_msg, str, Caps_off, Cap_clr_eol); 524 fflush(stdout); 525 sleep(MSG_SLEEP); 526 Msg_awaiting = 0; 527} 528 529 /* 530 * Show an input prompt + larger cursor */ 531static void show_pmt(const char *str) 532{ 533 PUTT("%s%s%s: %s%s", tg2(0, Msg_row) 534 , Curwin->capclr_pmt, str, Cap_curs_huge, Caps_off); 535 fflush(stdout); 536} 537 538 /* 539 * Show lines with specially formatted elements, but only output 540 * what will fit within the current screen width. 541 * Our special formatting consists of: 542 * "some text <_delimiter_> some more text <_delimiter_>...\n" 543 * Where <_delimiter_> is a single byte in the range of: 544 * \01 through \10 (in decimalizee, 1 - 8) 545 * and is used to select an 'attribute' from a capabilities table 546 * which is then applied to the *preceding* substring. 547 * Once recognized, the delimiter is replaced with a null character 548 * and viola, we've got a substring ready to output! Strings or 549 * substrings without delimiters will receive the Cap_norm attribute. 550 * 551 * Caution: 552 * This routine treats all non-delimiter bytes as displayable 553 * data subject to our screen width marching orders. If callers 554 * embed non-display data like tabs or terminfo strings in our 555 * glob, a line will truncate incorrectly at best. Worse case 556 * would be truncation of an embedded tty escape sequence. 557 * 558 * Tabs must always be avoided or our efforts are wasted and 559 * lines will wrap. To lessen but not eliminate the risk of 560 * terminfo string truncation, such non-display stuff should 561 * be placed at the beginning of a "short" line. 562 * (and as for tabs, gimme 1 more color then no worries, mate) */ 563static void show_special(int interact, const char *glob) 564{ /* note: the following is for documentation only, 565 the real captab is now found in a group's WIN_t ! 566 +------------------------------------------------------+ 567 | char *captab[] = { : Cap's/Delim's | 568 | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 | 569 | Sum_color, = \03 | 570 | Msg_color, Pmt_color, = \04, \05 | 571 | Hdr_color, = \06 | 572 | Row_color_high, = \07 | 573 | Row_color_norm }; = \10 [octal!] | 574 +------------------------------------------------------+ */ 575 char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ] 576 , *rp, *cap, *lin_end, *sub_beg, *sub_end; 577 int room; 578 579 /* handle multiple lines passed in a bunch */ 580 while ((lin_end = strchr(glob, '\n'))) { 581 582 /* create a local copy we can extend and otherwise abuse */ 583 memcpy(lin, glob, (unsigned)(lin_end - glob)); 584 /* zero terminate this part and prepare to parse substrings */ 585 lin[lin_end - glob] = '\0'; 586 room = Screen_cols; 587 sub_beg = sub_end = lin; 588 *(rp = row) = '\0'; 589 590 while (*sub_beg) { 591 switch (*sub_end) { 592 case 0: /* no end delim, captab makes normal */ 593 *(sub_end + 1) = '\0'; /* extend str end, then fall through */ 594 case 1 ... 8: 595 cap = Curwin->captab[(int)*sub_end]; 596 *sub_end = '\0'; 597 snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, 598 room, sub_beg, Caps_off); 599 rp = scat(rp, tmp); 600 room -= (sub_end - sub_beg); 601 sub_beg = ++sub_end; 602 break; 603 default: /* nothin' special, just text */ 604 ++sub_end; 605 } 606 if (unlikely(0 >= room)) 607 break; /* skip substrings that won't fit */ 608 } 609 610 if (interact) 611 PUTT("%s%s\n", row, Cap_clr_eol); 612 else 613 PUFF("%s%s\n", row, Cap_clr_eol); 614 glob = ++lin_end; /* point to next line (maybe) */ 615 } /* end: while 'lines' */ 616 617 /* If there's anything left in the glob (by virtue of no trailing '\n'), 618 it probably means caller wants to retain cursor position on this final 619 line. That, in turn, means we're interactive and so we'll just do our 620 'fit-to-screen' thingy... */ 621 if (*glob) 622 PUTT("%.*s", Screen_cols, glob); 623} 624 625/*###### Small Utility routines ########################################*/ 626 627 /* 628 * Get a string from the user */ 629static char *ask4str(const char *prompt) 630{ 631 static char buf[GETBUFSIZ]; 632 633 show_pmt(prompt); 634 memset(buf, '\0', sizeof(buf)); 635 chin(1, buf, sizeof(buf) - 1); 636 putp(Cap_curs_norm); 637 return strim_0(buf); 638} 639 640 /* 641 * Get a float from the user */ 642static float get_float(const char *prompt) 643{ 644 char *line; 645 float f; 646 647 if (!(*(line = ask4str(prompt)))) 648 return -1; 649 // note: we're not allowing negative floats 650 if (strcspn(line, ",.1234567890")) { 651 show_msg("\aNot valid"); 652 return -1; 653 } 654 sscanf(line, "%f", &f); 655 return f; 656} 657 658 /* 659 * Get an integer from the user */ 660static int get_int(const char *prompt) 661{ 662 char *line; 663 int n; 664 665 if (!(*(line = ask4str(prompt)))) 666 return -1; 667 // note: we've got to allow negative ints (renice) 668 if (strcspn(line, "-1234567890")) { 669 show_msg("\aNot valid"); 670 return -1; 671 } 672 sscanf(line, "%d", &n); 673 return n; 674} 675 676 /* 677 * Do some scaling stuff. 678 * We'll interpret 'num' as one of the following types and 679 * try to format it to fit 'width'. 680 * SK_no (0) it's a byte count 681 * SK_Kb (1) it's kilobytes 682 * SK_Mb (2) it's megabytes 683 * SK_Gb (3) it's gigabytes */ 684static const char *scale_num(unsigned num, const int width, const unsigned type) 685{ 686 /* kilobytes, megabytes, gigabytes, duh! */ 687 static float scale[] = { 1024, 1024 * 1024, 1024 * 1024 * 1024, 0 }; 688 /* kilo, mega, giga, none */ 689#ifdef CASEUP_SCALE 690 static char nextup[] = { 'K', 'M', 'G', 0 }; 691#else 692 static char nextup[] = { 'k', 'm', 'g', 0 }; 693#endif 694 static char buf[TNYBUFSIZ]; 695 float *dp; 696 char *up; 697 698 /* try an unscaled version first... */ 699 if (width >= snprintf(buf, sizeof(buf), "%u", num)) 700 return buf; 701 702 /* now try successively higher types until it fits */ 703 for (up = nextup + type, dp = scale; *dp; ++dp, ++up) { 704 /* the most accurate version */ 705 if (width >= 706 snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up)) 707 return buf; 708 /* the integer version */ 709 if (width >= 710 snprintf(buf, sizeof(buf), "%d%c", (int)(num / *dp), *up)) 711 return buf; 712 } 713 /* well shoot, this outta' fit... */ 714 return "?"; 715} 716 717 /* 718 * Do some scaling stuff. 719 * format 'tics' to fit 'width'. */ 720static const char *scale_tics(TIC_t tics, const int width) 721{ 722#ifdef CASEUP_SCALE 723#define HH "%uH" 724#define DD "%uD" 725#define WW "%uW" 726#else 727#define HH "%uh" 728#define DD "%ud" 729#define WW "%uw" 730#endif 731 static char buf[TNYBUFSIZ]; 732 unsigned long nt; // narrow time, for speed on 32-bit 733 unsigned cc; // centiseconds 734 unsigned nn; // multi-purpose whatever 735 736 nt = (tics * 100ull) / Hertz; 737 cc = nt % 100; // centiseconds past second 738 nt /= 100; // total seconds 739 nn = nt % 60; // seconds past the minute 740 nt /= 60; // total minutes 741 if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc)) 742 return buf; 743 if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) 744 return buf; 745 nn = nt % 60; // minutes past the hour 746 nt /= 60; // total hours 747 if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) 748 return buf; 749 nn = nt; // now also hours 750 if (width >= snprintf(buf, sizeof buf, HH, nn)) 751 return buf; 752 nn /= 24; // now days 753 if (width >= snprintf(buf, sizeof buf, DD, nn)) 754 return buf; 755 nn /= 7; // now weeks 756 if (width >= snprintf(buf, sizeof buf, WW, nn)) 757 return buf; 758 // well shoot, this outta' fit... 759 return "?"; 760 761#undef HH 762#undef DD 763#undef WW 764} 765 766#include <pwd.h> 767 768static int selection_type; 769static uid_t selection_uid; 770 771// FIXME: this is "temporary" code we hope 772static int good_uid(const proc_t * restrict const pp) 773{ 774 switch (selection_type) { 775 case 'p': 776 return 1; 777 case 0: 778 return 1; 779 case 'U': 780 if (pp->ruid == selection_uid) 781 return 1; 782 if (pp->suid == selection_uid) 783 return 1; 784 if (pp->fuid == selection_uid) 785 return 1; 786 // FALLTHROUGH 787 case 'u': 788 if (pp->euid == selection_uid) 789 return 1; 790 // FALLTHROUGH 791 default: 792 ; // don't know what it is; find bugs fast 793 } 794 return 0; 795} 796 797// swiped from ps, and ought to be in libproc 798static const char *parse_uid(const char *restrict const str, 799 uid_t * restrict const ret) 800{ 801 struct passwd *passwd_data; 802 char *endp; 803 unsigned long num; 804 static const char uidrange[] = "User ID out of range."; 805 static const char uidexist[] = "User name does not exist."; 806 num = strtoul(str, &endp, 0); 807 if (*endp != '\0') { /* hmmm, try as login name */ 808 passwd_data = getpwnam(str); 809 if (!passwd_data) 810 return uidexist; 811 num = passwd_data->pw_uid; 812 } 813 if (num > 0xfffffffeUL) 814 return uidrange; 815 *ret = num; 816 return 0; 817} 818 819/*###### Library Alternatives ##########################################*/ 820 821 /* 822 * Handle our own memory stuff without the risk of leaving the 823 * user's terminal in an ugly state should things go sour. */ 824 825static void *alloc_c(unsigned numb) MALLOC; 826static void *alloc_c(unsigned numb) 827{ 828 void *p; 829 830 if (!numb) 831 ++numb; 832 if (!(p = calloc(1, numb))) 833 std_err("failed memory allocate"); 834 return p; 835} 836 837static void *alloc_r(void *q, unsigned numb) MALLOC; 838static void *alloc_r(void *q, unsigned numb) 839{ 840 void *p; 841 842 if (!numb) 843 ++numb; 844 if (!(p = realloc(q, numb))) 845 std_err("failed memory allocate"); 846 return p; 847} 848 849 /* 850 * This guy's modeled on libproc's 'five_cpu_numbers' function except 851 * we preserve all cpu data in our CPU_t array which is organized 852 * as follows: 853 * cpus[0] thru cpus[n] == tics for each separate cpu 854 * cpus[Cpu_tot] == tics from the 1st /proc/stat line */ 855static CPU_t *cpus_refresh(CPU_t * cpus) 856{ 857 static FILE *fp = NULL; 858 int i; 859 // enough for a /proc/stat CPU line (not the intr line) 860 char buf[SMLBUFSIZ]; 861 862 /* by opening this file once, we'll avoid the hit on minor page faults 863 (sorry Linux, but you'll have to close it for us) */ 864 if (!fp) { 865 if (!(fp = fopen("/proc/stat", "r"))) 866 std_err(fmtmk 867 ("Failed /proc/stat open: %s", 868 strerror(errno))); 869 /* note: we allocate one more CPU_t than Cpu_tot so that the last slot 870 can hold tics representing the /proc/stat cpu summary (the first 871 line read) -- that slot supports our View_CPUSUM toggle */ 872 cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t)); 873 } 874 rewind(fp); 875 fflush(fp); 876 877 // first value the last slot with the cpu summary line 878 if (!fgets(buf, sizeof(buf), fp)) 879 std_err("failed /proc/stat read"); 880 if (4 > 881 sscanf(buf, CPU_FMTS_JUST1, &cpus[Cpu_tot].u, &cpus[Cpu_tot].n, 882 &cpus[Cpu_tot].s, &cpus[Cpu_tot].i, &cpus[Cpu_tot].w)) 883 std_err("failed /proc/stat read"); 884 // and just in case we're 2.2.xx compiled without SMP support... 885 if (1 == Cpu_tot) 886 memcpy(cpus, &cpus[1], sizeof(CPU_t)); 887 888 // now value each separate cpu's tics 889 for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { 890#ifdef PRETEND4CPUS 891 rewind(fp); 892#endif 893 if (!fgets(buf, sizeof(buf), fp)) 894 std_err("failed /proc/stat read"); 895 if (4 > 896 sscanf(buf, CPU_FMTS_MULTI, &cpus[i].u, &cpus[i].n, 897 &cpus[i].s, &cpus[i].i, &cpus[i].w)) 898 std_err("failed /proc/stat read"); 899 } 900 return cpus; 901} 902 903 /* 904 * Refresh procs *Helper* function to eliminate yet one more need 905 * to loop through our darn proc_t table. He's responsible for: 906 * 1) calculating the elapsed time since the previous frame 907 * 2) counting the number of tasks in each state (run, sleep, etc) 908 * 3) maintaining the HST_t's and priming the proc_t pcpu field 909 * 4) establishing the total number tasks for this frame */ 910static void prochlp(proc_t * this) 911{ 912 static HST_t *hist_sav = NULL; 913 static HST_t *hist_new = NULL; 914 static unsigned hist_siz = 0; // number of structs 915 static unsigned maxt_sav; // prior frame's max tasks 916 TIC_t tics; 917 918 if (unlikely(!this)) { 919 static struct timeval oldtimev; 920 struct timeval timev; 921 struct timezone timez; 922 HST_t *hist_tmp; 923 float et; 924 925 gettimeofday(&timev, &timez); 926 et = (timev.tv_sec - oldtimev.tv_sec) 927 + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0; 928 oldtimev.tv_sec = timev.tv_sec; 929 oldtimev.tv_usec = timev.tv_usec; 930 931 // if in Solaris mode, adjust our scaling for all cpus 932 Frame_tscale = 933 100.0f / ((float)Hertz * (float)et * 934 (Rc.mode_irixps ? 1 : Cpu_tot)); 935 maxt_sav = Frame_maxtask; 936 Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = 937 Frame_zombied = 0; 938 939 // reuse memory each time around 940 hist_tmp = hist_sav; 941 hist_sav = hist_new; 942 hist_new = hist_tmp; 943 // prep for our binary search by sorting the last frame's HST_t's 944 qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t) sort_HST_t); 945 return; 946 } 947 948 switch (this->state) { 949 case 'R': 950 Frame_running++; 951 break; 952 case 'S': 953 case 'D': 954 Frame_sleepin++; 955 break; 956 case 'T': 957 Frame_stopped++; 958 break; 959 case 'Z': 960 Frame_zombied++; 961 break; 962 } 963 964 if (unlikely(Frame_maxtask + 1 >= hist_siz)) { 965 hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25% 966 hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz); 967 hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz); 968 } 969 /* calculate time in this process; the sum of user time (utime) and 970 system time (stime) -- but PLEASE dont waste time and effort on 971 calcs and saves that go unused, like the old top! */ 972 hist_new[Frame_maxtask].pid = this->pid; 973 hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime); 974 975#if 0 976 { 977 int i; 978 int lo = 0; 979 int hi = maxt_sav - 1; 980 981 // find matching entry from previous frame and make ticks elapsed 982 while (lo <= hi) { 983 i = (lo + hi) / 2; 984 if (this->pid < hist_sav[i].pid) 985 hi = i - 1; 986 else if (likely(this->pid > hist_sav[i].pid)) 987 lo = i + 1; 988 else { 989 tics -= hist_sav[i].tics; 990 break; 991 } 992 } 993 } 994#else 995 { 996 HST_t tmp; 997 const HST_t *ptr; 998 tmp.pid = this->pid; 999 ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t); 1000 if (ptr) 1001 tics -= ptr->tics; 1002 } 1003#endif 1004 1005 // we're just saving elapsed tics, to be converted into %cpu if 1006 // this task wins it's displayable screen row lottery... */ 1007 this->pcpu = tics; 1008// if (Frames_maxcmdln) { } 1009 // shout this to the world with the final call (or us the next time in) 1010 Frame_maxtask++; 1011} 1012 1013 /* 1014 * This guy's modeled on libproc's 'readproctab' function except 1015 * we reuse and extend any prior proc_t's. He's been customized 1016 * for our specific needs and to avoid the use of <stdarg.h> */ 1017static proc_t **procs_refresh(proc_t ** table, int flags) 1018{ 1019#define PTRsz sizeof(proc_t *) 1020#define ENTsz sizeof(proc_t) 1021 static unsigned savmax = 0; // first time, Bypass: (i) 1022 proc_t *ptsk = (proc_t *) - 1; // first time, Force: (ii) 1023 unsigned curmax = 0; // every time (jeeze) 1024 PROCTAB *PT; 1025 1026 prochlp(NULL); // prep for a new frame 1027 if (Monpidsidx) 1028 PT = openproc(PROC_FILLBUG | PROC_PID, Monpids); 1029 else 1030 PT = openproc(flags); 1031 1032 // i) Allocated Chunks: *Existing* table; refresh + reuse 1033 while (curmax < savmax) { 1034 if (table[curmax]->cmdline) { 1035 free(*table[curmax]->cmdline); 1036 table[curmax]->cmdline = NULL; 1037 } 1038 if (unlikely(!(ptsk = readproc(PT, table[curmax])))) 1039 break; 1040 prochlp(ptsk); // tally & complete this proc_t 1041 ++curmax; 1042 } 1043 1044 // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill 1045 while (ptsk) { 1046 // realloc as we go, keeping 'table' ahead of 'currmax++' 1047 table = alloc_r(table, (curmax + 1) * PTRsz); 1048 // here, readproc will allocate the underlying proc_t stg 1049 if (likely(ptsk = readproc(PT, NULL))) { 1050 prochlp(ptsk); // tally & complete this proc_t 1051 table[curmax++] = ptsk; 1052 } 1053 } 1054 closeproc(PT); 1055 1056 // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists 1057 if (curmax >= savmax) { 1058 table = alloc_r(table, (curmax + 1) * PTRsz); 1059 // here, we must allocate the underlying proc_t stg ourselves 1060 table[curmax] = alloc_c(ENTsz); 1061 savmax = curmax + 1; 1062 } 1063 // this frame's end, but not necessarily end of allocated space 1064 table[curmax]->pid = -1; 1065 return table; 1066 1067#undef PTRsz 1068#undef ENTsz 1069} 1070 1071/*###### Field Table/RCfile compatability support ######################*/ 1072 1073 /* These are the Fieldstab.lflg values used here and in reframewins. 1074 (own identifiers as documentation and protection against changes) */ 1075#define L_stat PROC_FILLSTAT 1076#define L_statm PROC_FILLMEM 1077#define L_status PROC_FILLSTATUS 1078#define L_CMDLINE L_stat | PROC_FILLARG 1079#define L_EUSER PROC_FILLUSR 1080#define L_RUSER L_status | PROC_FILLUSR 1081#define L_GROUP L_status | PROC_FILLGRP 1082#define L_NONE 0 1083 // from either 'stat' or 'status' (preferred), via bits not otherwise used 1084#define L_EITHER PROC_SPARE_1 1085 // for reframewins and summary_show 1st pass 1086#define L_DEFAULT PROC_FILLSTAT 1087 1088 // a temporary macro, soon to be undef'd... 1089#define SF(f) (QFP_t)sort_P_ ## f 1090 1091 /* These are our gosh darn 'Fields' ! 1092 They MUST be kept in sync with pflags !! 1093 note: for integer data, the length modifiers found in .fmts may 1094 NOT reflect the true field type found in proc_t -- this plus 1095 a cast when/if displayed provides minimal width protection. */ 1096static FLD_t Fieldstab[] = { 1097/* .lflg anomolies: 1098 P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid) 1099 P_CPU, L_stat - never filled by libproc, but requires times (pcpu) 1100 P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline) 1101 L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit ! 1102 keys head fmts width scale sort desc lflg 1103 ------ ----------- ------- ------ ----- ----- ---------------------- -------- */ 1104 {"AaAa", " PID ", "%5u ", -1, -1, SF(PID), "Process Id", L_NONE}, 1105 {"BbBb", " PPID ", "%5u ", -1, -1, SF(PPD), "Parent Process Pid", 1106 L_EITHER}, 1107 {"CcQq", "RUSER ", "%-8.8s ", -1, -1, SF(URR), "Real user name", 1108 L_RUSER}, 1109 {"DdCc", " UID ", "%4u ", -1, -1, SF(UID), "User Id", L_NONE}, 1110 {"EeDd", "USER ", "%-8.8s ", -1, -1, SF(URE), "User Name", L_EUSER}, 1111 {"FfNn", "GROUP ", "%-8.8s ", -1, -1, SF(GRP), "Group Name", 1112 L_GROUP}, 1113 {"GgGg", "TTY ", "%-8.8s ", 8, -1, SF(TTY), "Controlling Tty", 1114 L_stat}, 1115 {"HhHh", " PR ", "%3d ", -1, -1, SF(PRI), "Priority", L_stat}, 1116 {"IiIi", " NI ", "%3d ", -1, -1, SF(NCE), "Nice value", L_stat}, 1117 {"JjYy", "#C ", "%2u ", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat}, 1118 {"KkEe", "%CPU ", "%#4.1f ", -1, -1, SF(CPU), "CPU usage", L_stat}, 1119 {"LlWw", " TIME ", "%6.6s ", 6, -1, SF(TME), "CPU Time", L_stat}, 1120 {"MmRr", " TIME+ ", "%9.9s ", 9, -1, SF(TME), "CPU Time, hundredths", 1121 L_stat}, 1122 {"NnFf", "%MEM ", "%#4.1f ", -1, -1, SF(RES), "Memory usage (RES)", 1123 L_statm}, 1124 {"OoMm", " VIRT ", "%5.5s ", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", 1125 L_statm}, 1126 {"PpOo", "SWAP ", "%4.4s ", 4, SK_Kb, SF(SWP), "Swapped size (kb)", 1127 L_statm}, 1128 {"QqTt", " RES ", "%4.4s ", 4, SK_Kb, SF(RES), "Resident size (kb)", 1129 L_statm}, 1130 {"RrKk", "CODE ", "%4.4s ", 4, SK_Kb, SF(COD), "Code size (kb)", 1131 L_statm}, 1132 {"SsLl", "DATA ", "%4.4s ", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", 1133 L_statm}, 1134 {"TtPp", " SHR ", "%4.4s ", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", 1135 L_statm}, 1136 {"UuJj", "nFLT ", "%4.4s ", 4, SK_no, SF(FLT), "Page Fault count", 1137 L_stat}, 1138 {"VvSs", "nDRT ", "%4.4s ", 4, SK_no, SF(DRT), "Dirty Pages count", 1139 L_statm}, 1140#ifdef USE_LIB_STA3 1141 {"WwVv", "STA ", "%3.3s ", -1, -1, SF(STA), "Process Status", L_stat}, 1142#else 1143 {"WwVv", "S ", "%c ", -1, -1, SF(STA), "Process Status", L_EITHER}, 1144#endif 1145 // next entry's special: '.head' will be formatted using table entry's own 1146 // '.fmts' plus runtime supplied conversion args! 1147 {"XxXx", "Command ", "%-*.*s ", -1, -1, SF(CMD), "Command name/line", 1148 L_EITHER}, 1149 {"YyUu", "WCHAN ", "%-9.9s ", -1, -1, SF(WCH), 1150 "Sleeping in Function", L_stat}, 1151 // next entry's special: the 0's will be replaced with '.'! 1152#ifdef CASEUP_HEXES 1153 {"ZzZz", "Flags ", "%08lX ", -1, -1, SF(FLG), "Task Flags <sched.h>", 1154 L_stat}, 1155#else 1156 {"ZzZz", "Flags ", "%08lx ", -1, -1, SF(FLG), "Task Flags <sched.h>", 1157 L_stat}, 1158#endif 1159#if 0 1160 {"..Qq", " A ", "%4.4s ", 4, SK_no, SF(PID), "Accessed Page count", 1161 L_stat}, 1162 {"..Nn", " TRS ", "%4.4s ", 4, SK_Kb, SF(PID), "Code in memory (kb)", 1163 L_stat}, 1164 {"..Rr", " WP ", "%4.4s ", 4, SK_no, SF(PID), "Unwritable Pages", 1165 L_stat}, 1166 {"Jj[{", "#C ", "%2u ", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat}, 1167 {"..\\|", "Bad ", "%2u ", -1, -1, SF(CPN), "-- must ignore | --", 0}, 1168 {"..]}", "Bad ", "%2u ", -1, -1, SF(CPN), "-- not used --", 0}, 1169 {"..^~", "Bad ", "%2u ", -1, -1, SF(CPN), "-- not used --", 0}, 1170#endif 1171}; 1172 1173#undef SF 1174 1175 /* All right, those-that-follow -- Listen Up! 1176 * For the above table keys and the following present/future rc file 1177 * compatibility support, you have Mr. Albert D. Cahalan to thank. 1178 * He must have been in a 'Christmas spirit'. Were it left to me, 1179 * this top would never have gotten that close to the former top's 1180 * crufty rcfile. Not only is it illogical, it's odoriferous ! 1181 */ 1182 1183 // used as 'to' and/or 'from' args in the ft_xxx utilities... 1184#define FT_NEW_fmt 0 1185#define FT_OLD_fmt 2 1186 1187#if 0 1188 // convert, or 0 for failure 1189static int ft_cvt_char(const int fr, const int to, int c) 1190{ 1191 int j = -1; 1192 1193 while (++j < MAXTBL(Fieldstab)) { 1194 if (c == Fieldstab[j].keys[fr]) 1195 return Fieldstab[j].keys[to]; 1196 if (c == Fieldstab[j].keys[fr + 1]) 1197 return Fieldstab[j].keys[to + 1]; 1198 } 1199 return 0; 1200} 1201#endif 1202 1203 // convert 1204static inline int ft_get_char(const int fr, int i) 1205{ 1206 int c; 1207 if (i < 0) 1208 return 0; 1209 if (i >= MAXTBL(Fieldstab)) 1210 return 0; 1211 c = Fieldstab[i].keys[fr]; 1212 if (c == '.') 1213 c = 0; // '.' marks a bad entry 1214 return c; 1215} 1216 1217#if 0 1218 // convert, or -1 for failure 1219static int ft_get_idx(const int fr, int c) 1220{ 1221 int j = -1; 1222 1223 while (++j < MAXTBL(Fieldstab)) { 1224 if (c == Fieldstab[j].keys[fr]) 1225 return j; 1226 if (c == Fieldstab[j].keys[fr + 1]) 1227 return j; 1228 } 1229 return -1; 1230} 1231#endif 1232 1233 // convert, or NULL for failure 1234static const FLD_t *ft_get_ptr(const int fr, int c) 1235{ 1236 int j = -1; 1237 1238 while (++j < MAXTBL(Fieldstab)) { 1239 if (c == Fieldstab[j].keys[fr]) 1240 return Fieldstab + j; 1241 if (c == Fieldstab[j].keys[fr + 1]) 1242 return Fieldstab + j; 1243 } 1244 return NULL; 1245} 1246 1247#if 0 1248 // convert, or NULL for failure 1249static const FLD_t *ft_idx_to_ptr(const int i) 1250{ 1251 if (i < 0) 1252 return NULL; 1253 if (i >= MAXTBL(Fieldstab)) 1254 return NULL; 1255 return Fieldstab + i; 1256} 1257 1258 // convert, or -1 for failure 1259static int ft_ptr_to_idx(const FLD_t * p) 1260{ 1261 int i; 1262 if (p < Fieldstab) 1263 return -1; 1264 i = p - Fieldstab; 1265 if (i >= MAXTBL(Fieldstab)) 1266 return -1; 1267 return i; 1268} 1269#endif 1270 1271#if 0 1272static void rc_bugless(const RCF_t * const rc) 1273{ 1274 const RCW_t *w; 1275 int i = 0; 1276 1277 fprintf(stderr, "\n%d %d %f %d\n", rc->mode_altscr, rc->mode_irixps, 1278 rc->delay_time, rc->win_index); 1279 while (i < 4) { 1280 w = &rc->win[i++]; 1281 fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n", 1282 w->winname, w->fieldscur, w->sortindx, w->winflags, 1283 w->maxtasks, w->summclr, w->msgsclr, w->headclr, 1284 w->taskclr); 1285 } 1286} 1287#endif 1288 1289 /* 1290 * '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window. 1291 * line 1: an eyecatcher, with a shameless advertisement 1292 * line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin. 1293 * For each of the 4 windows: 1294 * line a: contains winname, fieldscur 1295 * line b: contains winflags, sortindx, maxtasks 1296 * line c: contains summclr, msgsclr, headclr, taskclr 1297 * line d: if present, would crash procps-3.1.1 1298 */ 1299static int rc_read_new(const char *const buf, RCF_t * rc) 1300{ 1301 int i; 1302 int cnt; 1303 const char *cp; 1304 1305 cp = strstr(buf, "\n\n" RCF_EYECATCHER); 1306 if (!cp) 1307 return -1; 1308 cp = strchr(cp + 2, '\n'); 1309 if (!cp++) 1310 return -2; 1311 1312 cnt = 1313 sscanf(cp, 1314 "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n", 1315 &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, 1316 &rc->win_index); 1317 if (cnt != 4) 1318 return -3; 1319 cp = strchr(cp, '\n'); 1320 if (!cp++) 1321 return -4; 1322 1323 for (i = 0; i < GROUPSMAX; i++) { 1324 RCW_t *ptr = &rc->win[i]; 1325 cnt = 1326 sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, 1327 ptr->fieldscur); 1328 if (cnt != 2) 1329 return 5 + 100 * i; // OK to have less than 4 windows 1330 if (WINNAMSIZ <= strlen(ptr->winname)) 1331 return -6; 1332 if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) 1333 return -7; 1334 cp = strchr(cp, '\n'); 1335 if (!cp++) 1336 return -(8 + 100 * i); 1337 1338 cnt = 1339 sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n", 1340 &ptr->winflags, &ptr->sortindx, &ptr->maxtasks); 1341 if (cnt != 3) 1342 return -(9 + 100 * i); 1343 cp = strchr(cp, '\n'); 1344 if (!cp++) 1345 return -(10 + 100 * i); 1346 1347 cnt = 1348 sscanf(cp, 1349 "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n", 1350 &ptr->summclr, &ptr->msgsclr, &ptr->headclr, 1351 &ptr->taskclr); 1352 if (cnt != 4) 1353 return -(11 + 100 * i); 1354 cp = strchr(cp, '\n'); 1355 if (!cp++) 1356 return -(12 + 100 * i); 1357 while (*cp == '\t') { // skip unknown per-window settings 1358 cp = strchr(cp, '\n'); 1359 if (!cp++) 1360 return -(13 + 100 * i); 1361 } 1362 } 1363 return 13; 1364} 1365 1366static int rc_read_old(const char *const buf, RCF_t * rc) 1367{ 1368 const char std[] = 1369 "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......"; 1370 const char old[] = 1371 "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr"; 1372 unsigned u; 1373 const char *cp; 1374 unsigned c_show = 0; 1375 int badchar = 0; // allow a limited number of duplicates and junk 1376 1377 char scoreboard[256]; 1378 memset(scoreboard, '\0', sizeof scoreboard); 1379 1380 cp = buf + 2; // skip the "\n\n" we stuck at the beginning 1381 u = 0; 1382 for (;;) { 1383 const char *tmp; 1384 int c = *cp++; 1385 if (u + 1 >= sizeof rc->win[0].fieldscur) 1386 return -1; 1387 if (c == '\0') 1388 return -2; 1389 if (c == '\n') 1390 break; 1391 if (c & ~0x7f) 1392 return -3; 1393 if (~c & 0x20) 1394 c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden 1395 if (scoreboard[c | 0xe0u]) 1396 badchar++; // duplicates not allowed 1397 scoreboard[c | 0xe0u]++; 1398 tmp = strchr(old, c); 1399 if (!tmp) 1400 continue; 1401 c = *((tmp - old) + std); 1402 if (c == '.') 1403 continue; 1404 if (scoreboard[c & 0x1fu]) 1405 badchar++; // duplicates not allowed 1406 scoreboard[c & 0x1fu]++; 1407 rc->win[0].fieldscur[u++] = c; 1408 } 1409 rc->win[0].fieldscur[u++] = '\0'; 1410 if (u < 21) 1411 return -6; // catch junk, not good files (had 23 chars in one) 1412 if (u > 33) 1413 return -7; // catch junk, not good files (had 29 chars in one) 1414// fprintf(stderr, "badchar: %d\n", badchar); sleep(2); 1415 if (badchar > 8) 1416 return -8; // too much junk 1417 if (!c_show) 1418 return -9; // nothing was shown 1419 1420 // rest of file is optional, but better look right if it exists 1421 if (!*cp) 1422 return 12; 1423 if (*cp < '2' || *cp > '9') 1424 return -13; // stupid, and why isn't '1' valid? 1425 rc->delay_time = *cp - '0'; 1426 1427 memset(scoreboard, '\0', sizeof(scoreboard)); 1428 for (;;) { 1429 int c = *++cp & 0xffu; // protect scoreboard[] from negative char 1430 if (!c) 1431 return -14; // not OK to hit EOL w/o '\n' 1432 if (c == '\n') 1433 break; 1434 switch (c) { 1435 case ' ': 1436 case '.': 1437 case '0' ... '9': 1438 return -15; // not supposed to have digits here 1439 1440// case 's': // mostly for global rcfile 1441// rc->mode_secure = 1; 1442// break; 1443 case 'S': 1444 rc->win[0].winflags |= Show_CTIMES; 1445 break; 1446 case 'c': 1447 rc->win[0].winflags |= Show_CMDLIN; 1448 break; 1449 case 'i': 1450 rc->win[0].winflags &= ~Show_IDLEPS; 1451 break; 1452 case 'H': // 'H' = show threads (yea, sure) 1453 //rc->win[0].winflags |= ; 1454 break; 1455 case 'm': 1456 rc->win[0].winflags &= ~View_MEMORY; 1457 break; 1458 case 'l': 1459 rc->win[0].winflags &= ~View_LOADAV; 1460 break; 1461 case 't': 1462 rc->win[0].winflags &= ~View_STATES; 1463 break; 1464 case 'I': 1465 rc->mode_irixps = 0; 1466 break; 1467 1468 case 'M': 1469 c = 0; // for scoreboard 1470 rc->win[0].sortindx = P_MEM; 1471 break; 1472 case 'P': 1473 c = 0; // for scoreboard 1474 rc->win[0].sortindx = P_CPU; 1475 break; 1476 case 'A': // supposed to be start_time 1477 c = 0; // for scoreboard 1478 rc->win[0].sortindx = P_PID; 1479 break; 1480 case 'T': 1481 c = 0; // for scoreboard 1482 rc->win[0].sortindx = P_TM2; 1483 break; 1484 case 'N': 1485 c = 0; // for scoreboard 1486 rc->win[0].sortindx = P_PID; 1487 break; 1488 1489 default: 1490 // just ignore it, except for the scoreboard of course 1491 break; 1492 } 1493 if (scoreboard[c]) 1494 return -16; // duplicates not allowed 1495 scoreboard[c] = 1; 1496 } 1497 return 17; 1498} 1499 1500static void rc_write_new(FILE * fp) 1501{ 1502 int i; 1503 1504 fprintf(fp, 1505 RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n", 1506 Myname); 1507 fprintf(fp, 1508 RCF_DEPRECATED 1509 "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%d\n", 1510 Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, Curwin - Winstk); 1511 for (i = 0; i < GROUPSMAX; i++) { 1512 char buf[40]; 1513 char *cp = Winstk[i].rc.fieldscur; 1514 int j = 0; 1515 1516 while (j < 36) { 1517 int c = *cp++ & 0xff; 1518 switch (c) { 1519 case '.': 1520 case 1 ... ' ': 1521 case 0x7f ... 0xff: 1522 continue; // throw away junk (some of it) 1523 default: 1524 buf[j++] = c; // gets the '\0' too 1525 } 1526 if (!c) 1527 break; 1528 } 1529 fprintf(fp, "%s\tfieldscur=%s\n", Winstk[i].rc.winname, buf); 1530 fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n", 1531 Winstk[i].rc.winflags, Winstk[i].rc.sortindx, 1532 Winstk[i].rc.maxtasks); 1533 fprintf(fp, 1534 "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n", 1535 Winstk[i].rc.summclr, Winstk[i].rc.msgsclr, 1536 Winstk[i].rc.headclr, Winstk[i].rc.taskclr); 1537 } 1538} 1539 1540#if 0 1541static void rc_write_old(FILE * fp) 1542{ 1543 char buf[SMLBUFSIZ]; 1544 char *cp = Curwin->rc.fieldscur; 1545 int j = 0; 1546 int tmp; 1547 1548 while (j < 36) { 1549 int c = *cp++ & 0xff; 1550 if (c == 'M') 1551 c = 'L'; 1552 if (c == 'm') 1553 c = 'l'; 1554 switch (c) { 1555 case '.': 1556 case 'x': // try not to crash Rik's top (move COMMAND) 1557 case 'X': // try not to crash Rik's top (move COMMAND) 1558 case 1 ... ' ': 1559 case 0x7f ... 0xff: 1560 continue; // throw away junk (some of it) 1561 default: 1562 c = ft_cvt_char(FT_NEW_fmt, FT_OLD_fmt, c); 1563 if (!c) 1564 continue; // skip one we can't represent 1565 break; 1566 case '\0': 1567 buf[j++] = 'X'; // try not to crash Rik's top (move COMMAND) 1568 break; 1569 } 1570 buf[j++] = c; 1571 if (!c) 1572 break; 1573 } 1574 1575 fprintf(fp, "%s\n", buf); 1576 cp = buf; 1577 1578 tmp = (int)(Rc.delay_time + 0.5); 1579 if (tmp < 2) 1580 tmp = 2; 1581 if (tmp > 9) 1582 tmp = 9; 1583 *cp++ = tmp + '0'; 1584 1585 tmp = Curwin->rc.winflags; 1586// if (Secure_mode) *cp++ = 's'; // stupid to have in local rcfile 1587 if (tmp & Show_CTIMES) 1588 *cp++ = 'S'; 1589 if (tmp & Show_CMDLIN) 1590 *cp++ = 'c'; 1591 if (~tmp & Show_IDLEPS) 1592 *cp++ = 'i'; 1593// if () *cp++ = 'H'; // 'H' = show threads (yea, sure) 1594 if (~tmp & View_MEMORY) 1595 *cp++ = 'm'; 1596 if (~tmp & View_LOADAV) 1597 *cp++ = 'l'; 1598 if (~tmp & View_STATES) 1599 *cp++ = 't'; 1600 if (!Rc.mode_irixps) 1601 *cp++ = 'I'; 1602 1603 switch (Curwin->rc.sortindx) { 1604 case P_MEM: 1605 *cp++ = 'M'; 1606 break; 1607 case P_CPU: 1608 *cp++ = 'P'; 1609 break; 1610// case P_???: // was by start_time (non-display) 1611// *cp++ = 'A'; 1612// break; 1613 case P_TM2: 1614 *cp++ = 'T'; 1615 break; 1616 case P_PID: 1617 *cp++ = 'N'; 1618 break; 1619 } 1620 *cp++ = '\0'; 1621 fprintf(fp, "%s\n\n\n", buf); // important "\n\n" separator! 1622} 1623#endif 1624 1625static const char *rc_write_whatever(void) 1626{ 1627 FILE *fp = fopen(Rc_name, "w"); 1628 1629 if (!fp) 1630 return strerror(errno); 1631// if (Crufty_rcf) rc_write_old(fp); 1632 rc_write_new(fp); 1633 fclose(fp); 1634 return NULL; 1635} 1636 1637/*###### Startup routines ##############################################*/ 1638 1639#ifdef PRETEND4CPUS 1640#define smp_num_cpus 4 1641#endif 1642 1643 /* 1644 * No mater what *they* say, we handle the really really BIG and 1645 * IMPORTANT stuff upon which all those lessor functions depend! */ 1646static void before(char *me) 1647{ 1648 int i; 1649 1650 /* setup our program name -- big! */ 1651 Myname = strrchr(me, '/'); 1652 if (Myname) 1653 ++Myname; 1654 else 1655 Myname = me; 1656 1657 /* establish cpu particulars -- even bigger! */ 1658 Cpu_tot = smp_num_cpus; 1659 Cpu_map = alloc_r(NULL, sizeof(int) * Cpu_tot); 1660 for (i = 0; i < Cpu_tot; i++) 1661 Cpu_map[i] = i; 1662 if (linux_version_code > LINUX_VERSION(2, 5, 41)) 1663 States_fmts = STATES_line2x5; 1664 1665 /* get virtual page size -- nearing huge! */ 1666 Page_size = getpagesize(); 1667} 1668 1669 /* 1670 * Config file read *helper* function. 1671 * Anything missing won't show as a choice in the field editor, 1672 * so make sure there is exactly one of each letter. 1673 * 1674 * Due to Rik blindly accepting damem's broken patches, procps-2.0.1x 1675 * has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too. 1676 * Some people are maintainers, and others are human patchbots. 1677 * (thanks, Albert) */ 1678static void confighlp(char *fields) 1679{ 1680 unsigned upper[PFLAGSSIZ]; 1681 unsigned lower[PFLAGSSIZ]; 1682 char c; 1683 char *cp; 1684 1685 memset(upper, '\0', sizeof upper); 1686 memset(lower, '\0', sizeof lower); 1687 1688 cp = fields; 1689 for (;;) { 1690 c = *cp++; 1691 if (!c) 1692 break; 1693 if (isupper(c)) 1694 upper[c & 0x1f]++; 1695 else 1696 lower[c & 0x1f]++; 1697 } 1698 1699 c = 'a'; 1700 while (c <= 'z') { 1701 if (upper[c & 0x1f] && lower[c & 0x1f]) { 1702 lower[c & 0x1f] = 0; // got both, so wipe out unseen column 1703 for (;;) { 1704 cp = strchr(fields, c); 1705 if (cp) 1706 memmove(cp, cp + 1, strlen(cp)); 1707 else 1708 break; 1709 } 1710 } 1711 while (lower[c & 0x1f] > 1) { // got too many a..z 1712 lower[c & 0x1f]--; 1713 cp = strchr(fields, c); 1714 memmove(cp, cp + 1, strlen(cp)); 1715 } 1716 while (upper[c & 0x1f] > 1) { // got too many A..Z 1717 upper[c & 0x1f]--; 1718 cp = strchr(fields, toupper(c)); 1719 memmove(cp, cp + 1, strlen(cp)); 1720 } 1721 if (!upper[c & 0x1f] && !lower[c & 0x1f]) { // both missing 1722 lower[c & 0x1f]++; 1723 memmove(fields + 1, fields, strlen(fields) + 1); 1724 fields[0] = c; 1725 } 1726 c++; 1727 } 1728} 1729 1730 /* 1731 * First attempt to read the /etc/rcfile which contains two lines 1732 * consisting of the secure mode switch and an update interval. 1733 * It's presence limits what ordinary users are allowed to do. 1734 * (it's actually an old-style config file) 1735 * 1736 * Then build the local rcfile name and try to read a crufty old-top 1737 * rcfile (whew, odoriferous), which may contain an embedded new-style 1738 * rcfile. Whether embedded or standalone, new-style rcfile values 1739 * will always override that crufty stuff! 1740 * note: If running in secure mode via the /etc/rcfile, 1741 * Delay_time will be ignored except for root. */ 1742static void configs_read(void) 1743{ 1744 const RCF_t def_rcf = DEF_RCFILE; 1745 char fbuf[MEDBUFSIZ]; 1746 int i, fd; 1747 RCF_t rcf; 1748 float delay = Rc.delay_time; 1749 1750 // read part of an old-style config in /etc/toprc 1751 fd = open(SYS_RCFILESPEC, O_RDONLY); 1752 if (fd > 0) { 1753 ssize_t num; 1754 num = read(fd, fbuf, sizeof(fbuf) - 1); 1755 if (num > 0) { 1756 const char *sec = strchr(fbuf, 's'); 1757 const char *eol = strchr(fbuf, '\n'); 1758 if (eol) { 1759 const char *two = eol + 1; // line two 1760 if (sec < eol) 1761 Secure_mode = ! !sec; 1762 eol = strchr(two, '\n'); 1763 if (eol && eol > two && isdigit(*two)) 1764 Rc.delay_time = atof(two); 1765 } 1766 } 1767 close(fd); 1768 } 1769 1770 snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew... 1771 if (getenv("HOME")) 1772 snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), 1773 Myname); 1774 1775 rcf = def_rcf; 1776 fd = open(Rc_name, O_RDONLY); 1777 if (fd > 0) { 1778 ssize_t num; 1779 num = read(fd, fbuf + 2, sizeof(fbuf) - 3); 1780 if (num > 0) { 1781 fbuf[0] = '\n'; 1782 fbuf[1] = '\n'; 1783 fbuf[num + 2] = '\0'; 1784//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf)); 1785//sleep(2); 1786 if (rc_read_new(fbuf, &rcf) < 0) { 1787 rcf = def_rcf; // on failure, maybe mangled 1788 if (rc_read_old(fbuf, &rcf) < 0) 1789 rcf = def_rcf; 1790 } 1791 delay = rcf.delay_time; 1792 } 1793 close(fd); 1794 } 1795 // update Rc defaults, establish a Curwin and fix up the window stack 1796 Rc.mode_altscr = rcf.mode_altscr; 1797 Rc.mode_irixps = rcf.mode_irixps; 1798 if (rcf.win_index >= GROUPSMAX) 1799 rcf.win_index = 0; 1800 Curwin = &Winstk[rcf.win_index]; 1801 for (i = 0; i < GROUPSMAX; i++) { 1802 memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]); 1803 confighlp(Winstk[i].rc.fieldscur); 1804 } 1805 1806 // lastly, establish the true runtime secure mode and delay time 1807 if (!getuid()) 1808 Secure_mode = 0; 1809 if (!Secure_mode) 1810 Rc.delay_time = delay; 1811} 1812 1813 /* 1814 * Parse command line arguments. 1815 * Note: it's assumed that the rc file(s) have already been read 1816 * and our job is to see if any of those options are to be 1817 * overridden -- we'll force some on and negate others in our 1818 * best effort to honor the loser's (oops, user's) wishes... */ 1819#define BUFF_SIZE 256 1820static void parse_args(char **args) 1821{ 1822 /* differences between us and the former top: 1823 -o filename to output data to at each measurement interval 1824 -f filename to read output data from to calculate averages 1825 -C (separate CPU states for SMP) is left to an rcfile 1826 -p (pid monitoring) allows a comma delimited list 1827 -q (zero delay) eliminated as redundant, incomplete and inappropriate 1828 use: "nice -n-10 top -d0" to achieve what was only claimed 1829 -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility 1830 . no deprecated/illegal use of 'breakargv:' with goto 1831 . bunched args are actually handled properly and none are ignored 1832 . we tolerate NO whitespace and NO switches -- maybe too tolerant? */ 1833 static const char usage[] = 1834 " -hv | -bcisS -d delay -n iterations [-u user | -U user] -o filename -p pid [,pid ...] | -f filename"; 1835 float tmp_delay = MAXFLOAT; 1836 char *p; 1837 char buff[BUFF_SIZE]; 1838 int retcode; 1839 int loopcntr = 0; 1840 1841 int Task1, Task2, Task3, Task4, Task5; 1842 float AvgMaxTasks = 0.0, AvgRunningTasks = 0.0, AvgSleepingTasks = 1843 0.0, AvgStoppedTasks = 0.0, AvgZombieTasks = 0.0; 1844 1845 float CPU1, CPU2, CPU3, CPU4, CPU5; 1846 float AvgCPUuser = 0.0, AvgCPUsys = 0.0, AvgCPUnice = 0.0, AvgCPUidle = 1847 0.0, AvgCPUiowait = 0.0; 1848 1849 int Mem1, Mem2, Mem3, Mem4; 1850 int AvgMem1 = 0; 1851 long double AvgMem2 = 0.0, AvgMem3 = 0.0, AvgMem4 = 1852 0.0, UsedMemPercentage = 0.0; 1853 int AvgSwap1 = 0; 1854 long double AvgSwap2 = 0.0, AvgSwap3 = 0.0, AvgSwap4 = 1855 0.0, UsedSwapPercentage = 0.0; 1856 1857 while (*args) { 1858 const char *cp = *(args++); 1859 1860 while (*cp) { 1861 switch (*cp) { 1862 case '\0': 1863 case '-': 1864 break; 1865 case 'b': 1866 Batch = 1; 1867 break; 1868 case 'c': 1869 TOGw(Curwin, Show_CMDLIN); 1870 break; 1871 case 'd': 1872 if (cp[1]) 1873 ++cp; 1874 else if (*args) 1875 cp = *args++; 1876 else 1877 std_err("-d requires argument"); 1878 /* a negative delay will be dealt with shortly... */ 1879 if (1 != sscanf(cp, "%f", &tmp_delay)) 1880 std_err(fmtmk("bad delay '%s'", cp)); 1881 break; 1882 case 'f': 1883 if (cp[1]) 1884 cp++; 1885 else if (*args) 1886 cp = *args++; 1887 else 1888 std_err("-f requires argument"); 1889 if ((datafile = fopen(cp, "r")) == NULL) 1890 std_err(fmtmk 1891 ("bad file arg; failed to fopen '%s' for reading", 1892 cp)); 1893 retcode = 1894 fscanf(datafile, 1895 " MaxTasks:%d RunningTasks:%d SleepingTasks:%d StoppedTasks:%d ZombieTasks:%d", 1896 &Task1, &Task2, &Task3, &Task4, 1897 &Task5); 1898 while (retcode == 5) { 1899 loopcntr++; 1900 fgets(buff, BUFF_SIZE, datafile); 1901 AvgMaxTasks += Task1; 1902 AvgRunningTasks += Task2; 1903 AvgSleepingTasks += Task3; 1904 AvgStoppedTasks += Task4; 1905 AvgZombieTasks += Task5; 1906 fscanf(datafile, 1907 " Cpu(s): User:%f\tSystem:%f\tNice:%f\t\tIdle:%f\tIO-wait:%f", 1908 &CPU1, &CPU2, &CPU3, &CPU4, 1909 &CPU5); 1910 AvgCPUuser += CPU1; 1911 AvgCPUsys += CPU2; 1912 AvgCPUnice += CPU3; 1913 AvgCPUidle += CPU4; 1914 AvgCPUiowait += CPU5; 1915 fscanf(datafile, 1916 " TotalMem:%dk\tUsedMem:%dk\tFreeMem:%dk\t\tBuffers:%dk", 1917 &Mem1, &Mem2, &Mem3, &Mem4); 1918 fgets(buff, BUFF_SIZE, datafile); 1919 AvgMem1 = Mem1 / 1024; //this data should not change 1920 AvgMem2 += Mem2 / 1024; 1921 AvgMem3 += Mem3 / 1024; 1922 AvgMem4 += Mem4 / 1024; 1923 fscanf(datafile, 1924 " TotalSwap:%dk\tUsedSwap:%dk\tFreeSwap:%dk\tCached:%dk", 1925 &Mem1, &Mem2, &Mem3, &Mem4); 1926 fgets(buff, BUFF_SIZE, datafile); 1927 AvgSwap1 = Mem1 / 1024; //this data should not change 1928 AvgSwap2 += Mem2 / 1024; 1929 AvgSwap3 += Mem3 / 1024; 1930 AvgSwap4 += Mem4 / 1024; 1931 fgets(buff, BUFF_SIZE, datafile); 1932 retcode = 1933 fscanf(datafile, 1934 " MaxTasks:%d RunningTasks:%d SleepingTasks:%d StoppedTasks:%d ZombieTasks:%d", 1935 &Task1, &Task2, &Task3, 1936 &Task4, &Task5); 1937 } 1938 fclose(datafile); 1939 AvgMaxTasks = AvgMaxTasks / loopcntr; 1940 AvgRunningTasks = AvgRunningTasks / loopcntr; 1941 AvgSleepingTasks = AvgSleepingTasks / loopcntr; 1942 AvgStoppedTasks = AvgStoppedTasks / loopcntr; 1943 AvgZombieTasks = AvgZombieTasks / loopcntr; 1944 AvgCPUuser = AvgCPUuser / loopcntr; 1945 AvgCPUsys = AvgCPUsys / loopcntr; 1946 AvgCPUnice = AvgCPUnice / loopcntr; 1947 AvgCPUidle = AvgCPUidle / loopcntr; 1948 AvgCPUiowait = AvgCPUiowait / loopcntr; 1949 AvgMem1 = AvgMem1; 1950 AvgMem2 = AvgMem2 / loopcntr; 1951 AvgMem3 = AvgMem3 / loopcntr; 1952 AvgMem4 = AvgMem4 / loopcntr; 1953 AvgSwap1 = AvgSwap1; 1954 AvgSwap2 = AvgSwap2 / loopcntr; 1955 AvgSwap3 = AvgSwap3 / loopcntr; 1956 AvgSwap4 = AvgSwap4 / loopcntr; 1957 UsedMemPercentage = AvgMem2 / AvgMem1 * 100; 1958 UsedSwapPercentage = AvgSwap2 / AvgSwap1 * 100; 1959 1960 printf("\nAverage data from %s\n", cp); 1961 printf("================================\n"); 1962 printf 1963 (" MaxTasks:%.0f\t\tRunningTasks:%.0f\tSleepingTasks:%.0f\tStoppedTasks:%.0f\tZombieTasks:%.0f\n\n", 1964 AvgMaxTasks, AvgRunningTasks, 1965 AvgSleepingTasks, AvgStoppedTasks, 1966 AvgZombieTasks); 1967 printf 1968 (" Cpu(s) : User:%.2f\%\tSystem:%.2f\%\t\tNice:%.2f\%\t\tIdle:%.2f\%\t\tIO-wait:%.2f\%\n\n", 1969 AvgCPUuser, AvgCPUsys, AvgCPUnice, 1970 AvgCPUidle, AvgCPUiowait); 1971 printf 1972 (" TotalMem:%dMb\t\tUsedMem:%.0LfMb\t\tFreeMem:%.0LfMb\t\tBuffers:%.0LfMb\n", 1973 AvgMem1, AvgMem2, AvgMem3, AvgMem4); 1974 printf 1975 (" TotalSwap:%dMb\tUsedSwap:%.0LfMb\t\tFreeSwap:%.0LfMb\t\tCached:%.0LfMb\n", 1976 AvgSwap1, AvgSwap2, AvgSwap3, AvgSwap4); 1977 printf 1978 ("\n UsedMem Percentage:%.2Lf\%\tUsedSwap Percentage:%.2Lf\%\n\n", 1979 UsedMemPercentage, UsedSwapPercentage); 1980 printf 1981 ("A total of [%d] entries processed from %s.\n\n", 1982 loopcntr, cp); 1983 exit(0); 1984 break; 1985 case 'h': 1986 case 'H': 1987 case 'v': 1988 case 'V': 1989 std_err(fmtmk 1990 ("%s\nusage:\t%s%s", procps_version, 1991 Myname, usage)); 1992 case 'i': 1993 TOGw(Curwin, Show_IDLEPS); 1994 Curwin->rc.maxtasks = 0; 1995 break; 1996 case 'n': 1997 if (cp[1]) 1998 cp++; 1999 else if (*args) 2000 cp = *args++; 2001 else 2002 std_err("-n requires argument"); 2003 if (1 != sscanf(cp, "%d", &Loops) || 1 > Loops) 2004 std_err(fmtmk 2005 ("bad iterations arg '%s'", 2006 cp)); 2007 break; 2008 case 'o': 2009 o_flag = 0; 2010 if (cp[1]) 2011 cp++; 2012 else if (*args) 2013 cp = *args++; 2014 else 2015 std_err("-o requires argument"); 2016 if ((outfile = fopen(cp, "a")) == NULL) 2017 std_err(fmtmk 2018 ("bad file arg; failed to fopen '%s' for write", 2019 cp)); 2020 else 2021 o_flag = 1; 2022 cp = cp + strlen(cp); 2023 break; 2024 case 'p': 2025 do { 2026 if (selection_type) 2027 std_err 2028 ("conflicting process selection"); 2029 selection_type = 'p'; 2030 if (cp[1]) 2031 cp++; 2032 else if (*args) 2033 cp = *args++; 2034 else 2035 std_err("-p argument missing"); 2036 if (Monpidsidx >= MONPIDMAX) 2037 std_err(fmtmk 2038 ("pid limit (%d) exceeded", 2039 MONPIDMAX)); 2040 if (1 != 2041 sscanf(cp, "%d", 2042 &Monpids[Monpidsidx]) 2043 || 0 > Monpids[Monpidsidx]) 2044 std_err(fmtmk 2045 ("bad pid '%s'", cp)); 2046 if (!Monpids[Monpidsidx]) 2047 Monpids[Monpidsidx] = getpid(); 2048 Monpidsidx++; 2049 if (!(p = strchr(cp, ','))) 2050 break; 2051 cp = p; 2052 } while (*cp); 2053 break; 2054 case 's': 2055 Secure_mode = 1; 2056 break; 2057 case 'S': 2058 TOGw(Curwin, Show_CTIMES); 2059 break; 2060 case 'u': 2061 do { 2062 const char *errmsg; 2063 if (selection_type) 2064 std_err 2065 ("conflicting process selection"); 2066 if (cp[1]) 2067 cp++; 2068 else if (*args) 2069 cp = *args++; 2070 else 2071 std_err("-u missing name"); 2072 errmsg = parse_uid(cp, &selection_uid); 2073 if (errmsg) 2074 std_err(errmsg); 2075 selection_type = 'u'; 2076 cp += snprintf(Curwin->colusrnam, USRNAMSIZ - 1, "%s", cp); // FIXME: junk 2077 } while (0); 2078 break; 2079 case 'U': 2080 do { 2081 const char *errmsg; 2082 if (selection_type) 2083 std_err 2084 ("conflicting process selection"); 2085 if (cp[1]) 2086 cp++; 2087 else if (*args) 2088 cp = *args++; 2089 else 2090 std_err("-u missing name"); 2091 errmsg = parse_uid(cp, &selection_uid); 2092 if (errmsg) 2093 std_err(errmsg); 2094 selection_type = 'U'; 2095 cp += snprintf(Curwin->colusrnam, USRNAMSIZ - 1, "%s", cp); // FIXME: junk 2096 } while (0); 2097 break; 2098 default: 2099 std_err(fmtmk 2100 ("unknown argument '%c'\nusage:\t%s%s", 2101 *cp, Myname, usage)); 2102 2103 } /* end: switch (*cp) */ 2104 2105 /* advance cp and jump over any numerical args used above */ 2106 if (*cp) 2107 cp += strspn(&cp[1], "- ,.1234567890") + 1; 2108 } /* end: while (*cp) */ 2109 } /* end: while (*args) */ 2110 2111 /* fixup delay time, maybe... */ 2112 if (MAXFLOAT != tmp_delay) { 2113 if (Secure_mode || 0 > tmp_delay) 2114 msg_save("Delay time Not changed"); 2115 else 2116 Rc.delay_time = tmp_delay; 2117 } 2118} 2119 2120 /* 2121 * Set up the terminal attributes */ 2122static void whack_terminal(void) 2123{ 2124 struct termios newtty; 2125 2126 // the curses part... 2127#ifdef PRETENDNOCAP 2128 setupterm("dumb", STDOUT_FILENO, NULL); 2129#else 2130 setupterm(NULL, STDOUT_FILENO, NULL); 2131#endif 2132 // our part... 2133 if (!Batch) { 2134 if (-1 == tcgetattr(STDIN_FILENO, &Savedtty)) 2135 std_err("failed tty get"); 2136 newtty = Savedtty; 2137 newtty.c_lflag &= ~(ICANON | ECHO); 2138 newtty.c_oflag &= ~(TAB3); 2139 newtty.c_cc[VMIN] = 1; 2140 newtty.c_cc[VTIME] = 0; 2141 2142 Ttychanged = 1; 2143 if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty)) { 2144 putp(Cap_clr_scr); 2145 std_err(fmtmk("failed tty set: %s", strerror(errno))); 2146 } 2147 tcgetattr(STDIN_FILENO, &Rawtty); 2148#ifndef STDOUT_IOLBF 2149 // thanks anyway stdio, but we'll manage buffering at the frame level... 2150 setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf)); 2151#endif 2152 putp(Cap_clr_scr); 2153 fflush(stdout); 2154 } 2155} 2156 2157/*###### Field Selection/Ordering routines #############################*/ 2158 2159 /* 2160 * Display each field represented in the Fields Table along with its 2161 * description and mark (with a leading asterisk) fields associated 2162 * with upper case letter(s) in the passed 'fields string'. 2163 * 2164 * After all fields have been displayed, some extra explanatory 2165 * text may also be output */ 2166static void display_fields(const char *fields, const char *xtra) 2167{ 2168#define yRSVD 3 2169 const char *p; 2170 int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD; 2171 2172 /* we're relying on callers to first clear the screen and thus avoid screen 2173 flicker if they're too lazy to handle their own asterisk (*) logic */ 2174 putp(Curwin->cap_bold); 2175 for (i = 0; fields[i]; ++i) { 2176 const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]); 2177 int b = isupper(fields[i]); 2178 2179 if (!f) 2180 continue; // hey, should be std_err! 2181 for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces 2182 ; 2183 PUTT("%s%s%c %c: %-10s = %s", 2184 tg2((i / rmax) * cmax, (i % rmax) + yRSVD) 2185 , b ? Curwin->cap_bold : Cap_norm, b ? '*' : ' ', fields[i] 2186 , p, f->desc); 2187 } 2188 if (xtra) { 2189 putp(Curwin->capclr_rownorm); 2190 while ((p = strchr(xtra, '\n'))) { 2191 ++i; 2192 PUTT("%s%.*s", 2193 tg2((i / rmax) * cmax, (i % rmax) + yRSVD) 2194 , (int)(p - xtra) 2195 , xtra); 2196 xtra = ++p; 2197 } 2198 } 2199 putp(Caps_off); 2200 2201#undef yRSVD 2202} 2203 2204 /* 2205 * Change order of displayed fields. */ 2206static void fields_reorder(void) 2207{ 2208 static const char prompt[] = 2209 "Upper case letter moves field left, lower case right"; 2210 char c, *p; 2211 int i; 2212 2213 putp(Cap_clr_scr); 2214 putp(Cap_curs_huge); 2215 for (;;) { 2216 display_fields(Curwin->rc.fieldscur, FIELDS_xtra); 2217 show_special(1, 2218 fmtmk(FIELDS_current, Cap_home, 2219 Curwin->rc.fieldscur, Curwin->grpname, 2220 prompt)); 2221 chin(0, &c, 1); 2222 if (!ft_get_ptr(FT_NEW_fmt, c)) 2223 break; 2224 i = toupper(c) - 'A'; 2225 if (((p = strchr(Curwin->rc.fieldscur, i + 'A'))) 2226 || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) { 2227 if (isupper(c)) 2228 p--; 2229 if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) { 2230 c = p[0]; 2231 p[0] = p[1]; 2232 p[1] = c; 2233 } 2234 } 2235 } 2236 putp(Cap_curs_norm); 2237} 2238 2239 /* 2240 * Select sort field. */ 2241static void fields_sort(void) 2242{ 2243 static const char prompt[] = 2244 "Select sort field via field letter, type any other key to return"; 2245 char phoney[PFLAGSSIZ]; 2246 char c, *p; 2247 int i, x; 2248 2249 strcpy(phoney, NUL_FIELDS); 2250 x = i = Curwin->rc.sortindx; 2251 putp(Cap_clr_scr); 2252 putp(Cap_curs_huge); 2253 for (;;) { 2254 p = phoney + i; 2255 *p = toupper(*p); 2256 display_fields(phoney, SORT_xtra); 2257 show_special(1, 2258 fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, 2259 prompt)); 2260 chin(0, &c, 1); 2261 if (!ft_get_ptr(FT_NEW_fmt, c)) 2262 break; 2263 i = toupper(c) - 'A'; 2264 *p = tolower(*p); 2265 x = i; 2266 } 2267 if ((p = strchr(Curwin->rc.fieldscur, x + 'a'))) 2268 *p = x + 'A'; 2269 Curwin->rc.sortindx = x; 2270 putp(Cap_curs_norm); 2271} 2272 2273 /* 2274 * Toggle displayed fields. */ 2275static void fields_toggle(void) 2276{ 2277 static const char prompt[] = 2278 "Toggle fields via field letter, type any other key to return"; 2279 char c, *p; 2280 int i; 2281 2282 putp(Cap_clr_scr); 2283 putp(Cap_curs_huge); 2284 for (;;) { 2285 display_fields(Curwin->rc.fieldscur, FIELDS_xtra); 2286 show_special(1, 2287 fmtmk(FIELDS_current, Cap_home, 2288 Curwin->rc.fieldscur, Curwin->grpname, 2289 prompt)); 2290 chin(0, &c, 1); 2291 if (!ft_get_ptr(FT_NEW_fmt, c)) 2292 break; 2293 i = toupper(c) - 'A'; 2294 if ((p = strchr(Curwin->rc.fieldscur, i + 'A'))) 2295 *p = i + 'a'; 2296 else if ((p = strchr(Curwin->rc.fieldscur, i + 'a'))) 2297 *p = i + 'A'; 2298 } 2299 putp(Cap_curs_norm); 2300} 2301 2302/*###### Windows/Field Groups support #################################*/ 2303 2304 /* 2305 * For each of the four windows: 2306 * 1) Set the number of fields/columns to display 2307 * 2) Create the field columns heading 2308 * 3) Set maximum cmdline length, if command lines are in use 2309 * In the process, the required PROC_FILLxxx flags will be rebuilt! */ 2310static void reframewins(void) 2311{ 2312 WIN_t *w; 2313 char *s; 2314 const char *h; 2315 int i, needpsdb = 0; 2316 2317// Frames_libflags = 0; // should be called only when it's zero 2318// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible 2319 w = Curwin; 2320 do { 2321 if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) { 2322 // build window's procflags array and establish a tentative maxpflgs 2323 for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) { 2324 if (isupper(w->rc.fieldscur[i])) 2325 w->procflags[w->maxpflgs++] = 2326 w->rc.fieldscur[i] - 'A'; 2327 } 2328 2329 /* build a preliminary columns header not to exceed screen width 2330 while accounting for a possible leading window number */ 2331 *(s = w->columnhdr) = '\0'; 2332 if (Rc.mode_altscr) 2333 s = scat(s, " "); 2334 for (i = 0; i < w->maxpflgs; i++) { 2335 h = Fieldstab[w->procflags[i]].head; 2336 // oops, won't fit -- we're outta here... 2337 if (Screen_cols < 2338 (int)((s - w->columnhdr) + strlen(h))) 2339 break; 2340 s = scat(s, h); 2341 } 2342 2343 /* establish the final maxpflgs and prepare to grow the command column 2344 heading via maxcmdln - it may be a fib if P_CMD wasn't encountered, 2345 but that's ok because it won't be displayed anyway */ 2346 w->maxpflgs = i; 2347 w->maxcmdln = Screen_cols 2348 - (strlen(w->columnhdr) - 2349 strlen(Fieldstab[P_CMD].head)) - 1; 2350 2351 /* finally, we can build the true run-time columns header, format the 2352 command column heading, if P_CMD is really being displayed, and 2353 rebuild the all-important PROC_FILLxxx flags that will be used 2354 until/if we're we're called again */ 2355 *(s = w->columnhdr) = '\0'; 2356 if (Rc.mode_altscr) 2357 s = scat(s, fmtmk("%d", w->winnum)); 2358 for (i = 0; i < w->maxpflgs; i++) { 2359 h = Fieldstab[w->procflags[i]].head; 2360 if (P_WCH == w->procflags[i]) 2361 needpsdb = 1; 2362 if (P_CMD == w->procflags[i]) { 2363 s = scat(s, 2364 fmtmk(Fieldstab[P_CMD].fmts, 2365 w->maxcmdln, w->maxcmdln, 2366 h)); 2367 if (CHKw(w, Show_CMDLIN)) { 2368 Frames_libflags |= L_CMDLINE; 2369// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln; 2370 } 2371 } else 2372 s = scat(s, h); 2373 Frames_libflags |= 2374 Fieldstab[w->procflags[i]].lflg; 2375 } 2376 } 2377 if (Rc.mode_altscr) 2378 w = w->next; 2379 } while (w != Curwin); 2380 2381 // do we need the kernel symbol table (and is it already open?) 2382 if (needpsdb) { 2383 if (-1 == No_ksyms) { 2384 No_ksyms = 0; 2385 if (open_psdb_message(NULL, msg_save)) 2386 No_ksyms = 1; 2387 else 2388 PSDBopen = 1; 2389 } 2390 } 2391 2392 if (selection_type == 'U') 2393 Frames_libflags |= L_status; 2394 2395 if (Frames_libflags & L_EITHER) { 2396 Frames_libflags &= ~L_EITHER; 2397 if (!(Frames_libflags & L_stat)) 2398 Frames_libflags |= L_status; 2399 } 2400 if (!Frames_libflags) 2401 Frames_libflags = L_DEFAULT; 2402} 2403 2404 /* 2405 * Value a window's name and make the associated group name. */ 2406static void win_names(WIN_t * q, const char *name) 2407{ 2408 sprintf(q->rc.winname, "%.*s", WINNAMSIZ - 1, name); 2409 sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ - 1, name); 2410} 2411 2412 /* 2413 * Display a window/field group (ie. make it "current"). */ 2414static void win_select(char ch) 2415{ 2416 static const char prompt[] = "Choose field group (1 - 4)"; 2417 2418 /* if there's no ch, it means we're supporting the external interface, 2419 so we must try to get our own darn ch by begging the user... */ 2420 if (!ch) { 2421 show_pmt(prompt); 2422 chin(0, (char *)&ch, 1); 2423 } 2424 switch (ch) { 2425 case 'a': /* we don't carry 'a' / 'w' in our */ 2426 Curwin = Curwin->next; /* pmt - they're here for a good */ 2427 break; /* friend of ours -- wins_colors. */ 2428 case 'w': /* (however those letters work via */ 2429 Curwin = Curwin->prev; /* the pmt too but gee, end-loser */ 2430 break; /* should just press the darn key) */ 2431 case '1': 2432 case '2': 2433 case '3': 2434 case '4': 2435 Curwin = &Winstk[ch - '1']; 2436 break; 2437 } 2438} 2439 2440 /* 2441 * Just warn the user when a command can't be honored. */ 2442static int win_warn(void) 2443{ 2444 show_msg(fmtmk 2445 ("\aCommand disabled, activate %s with '-' or '_'", 2446 Curwin->grpname)); 2447 /* we gotta' return false 'cause we're somewhat well known within 2448 macro society, by way of that sassy little tertiary operator... */ 2449 return 0; 2450} 2451 2452 /* 2453 * Change colors *Helper* function to save/restore settings; 2454 * ensure colors will show; and rebuild the terminfo strings. */ 2455static void winsclrhlp(WIN_t * q, int save) 2456{ 2457 static int flgssav, summsav, msgssav, headsav, tasksav; 2458 2459 if (save) { 2460 flgssav = q->rc.winflags; 2461 summsav = q->rc.summclr; 2462 msgssav = q->rc.msgsclr; 2463 headsav = q->rc.headclr; 2464 tasksav = q->rc.taskclr; 2465 SETw(q, Show_COLORS); 2466 } else { 2467 q->rc.winflags = flgssav; 2468 q->rc.summclr = summsav; 2469 q->rc.msgsclr = msgssav; 2470 q->rc.headclr = headsav; 2471 q->rc.taskclr = tasksav; 2472 } 2473 capsmk(q); 2474} 2475 2476 /* 2477 * Change colors used in display */ 2478static void wins_colors(void) 2479{ 2480#define kbdABORT 'q' 2481#define kbdAPPLY '\n' 2482 int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr; 2483 char ch, tgt = 'T'; 2484 2485 if (0 >= max_colors) { 2486 show_msg("\aNo colors to map!"); 2487 return; 2488 } 2489 winsclrhlp(Curwin, 1); 2490 putp(Cap_clr_scr); 2491 putp(Cap_curs_huge); 2492 2493 do { 2494 putp(Cap_home); 2495 /* this string is well above ISO C89's minimum requirements! */ 2496 show_special(1, 2497 fmtmk(COLOR_help, procps_version, Curwin->grpname, 2498 CHKw(Curwin, View_NOBOLD) ? "On" : "Off", 2499 CHKw(Curwin, Show_COLORS) ? "On" : "Off", 2500 CHKw(Curwin, Show_HIBOLD) ? "On" : "Off", 2501 tgt, clr, Curwin->grpname)); 2502 chin(0, &ch, 1); 2503 switch (ch) { 2504 case 'S': 2505 pclr = &Curwin->rc.summclr; 2506 clr = *pclr; 2507 tgt = ch; 2508 break; 2509 case 'M': 2510 pclr = &Curwin->rc.msgsclr; 2511 clr = *pclr; 2512 tgt = ch; 2513 break; 2514 case 'H': 2515 pclr = &Curwin->rc.headclr; 2516 clr = *pclr; 2517 tgt = ch; 2518 break; 2519 case 'T': 2520 pclr = &Curwin->rc.taskclr; 2521 clr = *pclr; 2522 tgt = ch; 2523 break; 2524 case '0' ... '7': 2525 clr = ch - '0'; 2526 *pclr = clr; 2527 break; 2528 case 'B': 2529 TOGw(Curwin, View_NOBOLD); 2530 break; 2531 case 'b': 2532 TOGw(Curwin, Show_HIBOLD); 2533 break; 2534 case 'z': 2535 TOGw(Curwin, Show_COLORS); 2536 break; 2537 case 'a': 2538 case 'w': 2539 win_select(ch); 2540 winsclrhlp(Curwin, 1); 2541 clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr; 2542 tgt = 'T'; 2543 break; 2544 } 2545 capsmk(Curwin); 2546 } while (kbdAPPLY != ch && kbdABORT != ch); 2547 2548 if (kbdABORT == ch) 2549 winsclrhlp(Curwin, 0); 2550 putp(Cap_curs_norm); 2551 2552#undef kbdABORT 2553#undef kbdAPPLY 2554} 2555 2556 /* 2557 * Manipulate flag(s) for all our windows. */ 2558static void wins_reflag(int what, int flg) 2559{ 2560 WIN_t *w; 2561 2562 w = Curwin; 2563 do { 2564 switch (what) { 2565 case Flags_TOG: 2566 TOGw(w, flg); 2567 break; 2568 case Flags_SET: /* Ummmm, i can't find anybody */ 2569 SETw(w, flg); /* who uses Flags_set ... */ 2570 break; 2571 case Flags_OFF: 2572 OFFw(w, flg); 2573 break; 2574 } 2575 /* a flag with special significance -- user wants to rebalance 2576 display so we gotta' 'off' one number then force on two flags... */ 2577 if (EQUWINS_cwo == flg) { 2578 w->rc.maxtasks = 0; 2579 SETw(w, Show_IDLEPS | VISIBLE_tsk); 2580 } 2581 w = w->next; 2582 } while (w != Curwin); 2583} 2584 2585 /* 2586 * Set the screen dimensions and arrange for the real workhorse. 2587 * (also) catches: 2588 * SIGWINCH and SIGCONT */ 2589static void wins_resize(int dont_care_sig) 2590{ 2591 struct winsize wz; 2592 2593 (void)dont_care_sig; 2594 Screen_cols = columns; 2595 Screen_rows = lines; 2596 if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz))) { 2597 Screen_cols = wz.ws_col; 2598 Screen_rows = wz.ws_row; 2599 } 2600 if (Batch) 2601 Screen_rows = MAXINT; 2602 2603 // we might disappoint some folks (but they'll deserve it) 2604 if (SCREENMAX < Screen_cols) 2605 Screen_cols = SCREENMAX; 2606 2607 /* keep our support for output optimization in sync with current reality 2608 note: when we're in Batch mode, we don't really need a Pseudo_scrn and 2609 when not Batch, our buffer will contain 1 extra 'line' since 2610 Msg_row is never represented -- but it's nice to have some space 2611 between us and the great-beyond... */ 2612 Pseudo_cols = Screen_cols + CLRBUFSIZ + 1; 2613 if (Batch) 2614 Pseudo_size = ROWBUFSIZ + 1; 2615 else 2616 Pseudo_size = Pseudo_cols * Screen_rows; 2617 Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size); 2618 2619 // force rebuild of column headers AND libproc/readproc requirements 2620 Frames_libflags = 0; 2621} 2622 2623 /* 2624 * Set up the raw/incomplete field group windows -- 2625 * they'll be finished off after startup completes. 2626 * [ and very likely that will override most/all of our efforts ] 2627 * [ --- life-is-NOT-fair --- ] */ 2628static void windows_stage1(void) 2629{ 2630 WIN_t *w; 2631 int i; 2632 2633 for (i = 0; i < GROUPSMAX; i++) { 2634 w = &Winstk[i]; 2635 w->winnum = i + 1; 2636 w->rc = Rc.win[i]; 2637 w->captab[0] = Cap_norm; 2638 w->captab[1] = Cap_norm; 2639 w->captab[2] = w->cap_bold; 2640 w->captab[3] = w->capclr_sum; 2641 w->captab[4] = w->capclr_msg; 2642 w->captab[5] = w->capclr_pmt; 2643 w->captab[6] = w->capclr_hdr; 2644 w->captab[7] = w->capclr_rowhigh; 2645 w->captab[8] = w->capclr_rownorm; 2646 w->next = w + 1; 2647 w->prev = w - 1; 2648 ++w; 2649 } 2650 /* fixup the circular chains... */ 2651 Winstk[3].next = &Winstk[0]; 2652 Winstk[0].prev = &Winstk[3]; 2653 Curwin = Winstk; 2654} 2655 2656 /* 2657 * This guy just completes the field group windows after the 2658 * rcfiles have been read and command line arguments parsed */ 2659static void windows_stage2(void) 2660{ 2661 int i; 2662 2663 for (i = 0; i < GROUPSMAX; i++) { 2664 win_names(&Winstk[i], Winstk[i].rc.winname); 2665 capsmk(&Winstk[i]); 2666 } 2667 // rely on this next guy to force a call (eventually) to reframewins 2668 wins_resize(0); 2669} 2670 2671/*###### Main Screen routines ##########################################*/ 2672 2673 /* 2674 * Process keyboard input during the main loop */ 2675static void do_key(unsigned c) 2676{ 2677 // standardized 'secure mode' errors 2678 static const char err_secure[] = "\aUnavailable in secure mode"; 2679#ifdef WARN_NOT_SMP 2680 // standardized 'smp' errors 2681 static const char err_smp[] = "\aSorry, only 1 cpu detected"; 2682#endif 2683 2684 switch (c) { 2685 case '1': 2686#ifdef WARN_NOT_SMP 2687 if (Cpu_tot > 1) 2688 TOGw(Curwin, View_CPUSUM); 2689 else 2690 show_msg(err_smp); 2691#else 2692 TOGw(Curwin, View_CPUSUM); 2693#endif 2694 break; 2695 2696 case 'a': 2697 if (Rc.mode_altscr) 2698 Curwin = Curwin->next; 2699 break; 2700 2701 case 'A': 2702 Rc.mode_altscr = !Rc.mode_altscr; 2703 wins_resize(0); 2704 break; 2705 2706 case 'b': 2707 if (VIZCHKc) { 2708 if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS)) 2709 show_msg("\aNothing to highlight!"); 2710 else { 2711 TOGw(Curwin, Show_HIBOLD); 2712 capsmk(Curwin); 2713 } 2714 } 2715 break; 2716 2717 case 'B': 2718 TOGw(Curwin, View_NOBOLD); 2719 capsmk(Curwin); 2720 break; 2721 2722 case 'c': 2723 VIZTOGc(Show_CMDLIN); 2724 break; 2725 2726 case 'd': 2727 case 's': 2728 if (Secure_mode) 2729 show_msg(err_secure); 2730 else { 2731 float tmp = 2732 get_float(fmtmk 2733 ("Change delay from %.1f to", 2734 Rc.delay_time)); 2735 if (-1 < tmp) 2736 Rc.delay_time = tmp; 2737 } 2738 break; 2739 2740 case 'f': 2741 if (VIZCHKc) 2742 fields_toggle(); 2743 break; 2744 2745 case 'F': 2746 case 'O': 2747 if (VIZCHKc) 2748 fields_sort(); 2749 break; 2750 2751 case 'g': 2752 if (Rc.mode_altscr) { 2753 char tmp[GETBUFSIZ]; 2754 strcpy(tmp, 2755 ask4str(fmtmk 2756 ("Rename window '%s' to (1-3 chars)", 2757 Curwin->rc.winname))); 2758 if (tmp[0]) 2759 win_names(Curwin, tmp); 2760 } 2761 break; 2762 2763 case 'G': 2764 win_select(0); 2765 break; 2766 2767 case 'h': 2768 case '?': 2769 { 2770 char ch; 2771 putp(Cap_clr_scr); 2772 putp(Cap_curs_huge); 2773 /* this string is well above ISO C89's minimum requirements! */ 2774 show_special(1, 2775 fmtmk(KEYS_help, procps_version, 2776 Curwin->grpname, CHKw(Curwin, 2777 Show_CTIMES) ? 2778 "On" : "Off", Rc.delay_time, 2779 Secure_mode ? "On" : "Off", 2780 Secure_mode ? "" : 2781 KEYS_help_unsecured)); 2782 chin(0, &ch, 1); 2783 if ('?' == ch || 'h' == ch) { 2784 do { 2785 putp(Cap_clr_scr); 2786 show_special(1, 2787 fmtmk(WINDOWS_help, 2788 Curwin->grpname, 2789 Winstk[0].rc.winname, 2790 Winstk[1].rc.winname, 2791 Winstk[2].rc.winname, 2792 Winstk[3].rc. 2793 winname)); 2794 chin(0, &ch, 1); 2795 win_select(ch); 2796 } while ('\n' != ch); 2797 } 2798 putp(Cap_curs_norm); 2799 } 2800 break; 2801 2802 case 'i': 2803 VIZTOGc(Show_IDLEPS); 2804 break; 2805 2806 case 'I': 2807#ifdef WARN_NOT_SMP 2808 if (Cpu_tot > 1) { 2809 Rc.mode_irixps = !Rc.mode_irixps; 2810 show_msg(fmtmk 2811 ("Irix mode %s", 2812 Rc.mode_irixps ? "On" : "Off")); 2813 } else 2814 show_msg(err_smp); 2815#else 2816 Rc.mode_irixps = !Rc.mode_irixps; 2817 show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); 2818#endif 2819 break; 2820 2821 case 'k': 2822 if (Secure_mode) { 2823 show_msg(err_secure); 2824 } else { 2825 int sig, pid = get_int("PID to kill"); 2826 if (0 < pid) { 2827 sig = 2828 signal_name_to_number(ask4str 2829 (fmtmk 2830 ("Kill PID %d with signal [%i]", 2831 pid, DEF_SIGNAL))); 2832 if (-1 == sig) 2833 sig = DEF_SIGNAL; 2834 if (sig && kill(pid, sig)) 2835 show_msg(fmtmk 2836 ("\aKill of PID '%d' with '%d' failed: %s", 2837 pid, sig, strerror(errno))); 2838 } 2839 } 2840 break; 2841 2842 case 'l': 2843 TOGw(Curwin, View_LOADAV); 2844 break; 2845 2846 case 'm': 2847 TOGw(Curwin, View_MEMORY); 2848 break; 2849 2850 case 'n': 2851 case '#': 2852 if (VIZCHKc) { 2853 int num = 2854 get_int(fmtmk 2855 ("Maximum tasks = %d, change to (0 is unlimited)", 2856 Curwin->rc.maxtasks)); 2857 if (-1 < num) 2858 Curwin->rc.maxtasks = num; 2859 } 2860 break; 2861 2862 case 'o': 2863 if (VIZCHKc) 2864 fields_reorder(); 2865 break; 2866 2867 case 'q': 2868 end_pgm(0); 2869 2870 case 'r': 2871 if (Secure_mode) 2872 show_msg(err_secure); 2873 else { 2874 int val, pid = get_int("PID to renice"); 2875 if (0 < pid) { 2876 val = 2877 get_int(fmtmk 2878 ("Renice PID %d to value", pid)); 2879 if (setpriority 2880 (PRIO_PROCESS, (unsigned)pid, val)) 2881 show_msg(fmtmk 2882 ("\aRenice of PID %d to %d failed: %s", 2883 pid, val, strerror(errno))); 2884 } 2885 } 2886 break; 2887 2888 case 'R': 2889 VIZTOGc(Qsrt_NORMAL); 2890 break; 2891 2892 case 'S': 2893 if (VIZCHKc) { 2894 TOGw(Curwin, Show_CTIMES); 2895 show_msg(fmtmk 2896 ("Cumulative time %s", 2897 CHKw(Curwin, Show_CTIMES) ? "On" : "Off")); 2898 } 2899 break; 2900 2901 case 't': 2902 TOGw(Curwin, View_STATES); 2903 break; 2904 2905// case 'u': 2906// if (VIZCHKc) 2907// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)")); 2908// break; 2909 2910 case 'u': 2911// if (!VIZCHKc) break; 2912 do { 2913 const char *errmsg; 2914 const char *answer; 2915 answer = ask4str("Which user (blank for all)"); 2916 // FIXME: do this better: 2917 if (!answer || *answer == '\0' || *answer == '\n' 2918 || *answer == '\r' || *answer == '\t' 2919 || *answer == ' ') { 2920 selection_type = 0; 2921 selection_uid = -1; 2922 break; 2923 } 2924 errmsg = parse_uid(answer, &selection_uid); 2925 if (errmsg) { 2926 show_msg(errmsg); 2927 // Change settings here? I guess not. 2928 break; 2929 } 2930 selection_type = 'u'; 2931 } while (0); 2932 break; 2933 2934 case 'U': 2935// if (!VIZCHKc) break; 2936 do { 2937 const char *errmsg; 2938 const char *answer; 2939 answer = ask4str("Which user (blank for all)"); 2940 // FIXME: do this better: 2941 if (!answer || *answer == '\0' || *answer == '\n' 2942 || *answer == '\r' || *answer == '\t' 2943 || *answer == ' ') { 2944 selection_type = 0; 2945 selection_uid = -1; 2946 break; 2947 } 2948 errmsg = parse_uid(answer, &selection_uid); 2949 if (errmsg) { 2950 show_msg(errmsg); 2951 // Change settings here? I guess not. 2952 break; 2953 } 2954 selection_type = 'U'; 2955 } while (0); 2956 break; 2957 2958 case 'w': 2959 if (Rc.mode_altscr) 2960 Curwin = Curwin->prev; 2961 break; 2962 2963 case 'W': 2964 { 2965 const char *err = rc_write_whatever(); 2966 if (err) 2967 show_msg(fmtmk 2968 ("\aFailed '%s' open: %s", Rc_name, 2969 err)); 2970 else 2971 show_msg(fmtmk 2972 ("Wrote configuration to '%s'", 2973 Rc_name)); 2974 } 2975 break; 2976 2977 case 'x': 2978 if (VIZCHKc) { 2979 TOGw(Curwin, Show_HICOLS); 2980 capsmk(Curwin); 2981 } 2982 break; 2983 2984 case 'y': 2985 if (VIZCHKc) { 2986 TOGw(Curwin, Show_HIROWS); 2987 capsmk(Curwin); 2988 } 2989 break; 2990 2991 case 'z': 2992 if (VIZCHKc) { 2993 TOGw(Curwin, Show_COLORS); 2994 capsmk(Curwin); 2995 } 2996 break; 2997 2998 case 'Z': 2999 wins_colors(); 3000 break; 3001 3002 case '-': 3003 if (Rc.mode_altscr) 3004 TOGw(Curwin, VISIBLE_tsk); 3005 break; 3006 3007 case '_': 3008 if (Rc.mode_altscr) 3009 wins_reflag(Flags_TOG, VISIBLE_tsk); 3010 break; 3011 3012 case '=': 3013 Curwin->rc.maxtasks = 0; 3014 SETw(Curwin, Show_IDLEPS | VISIBLE_tsk); 3015 Monpidsidx = 0; 3016 break; 3017 3018 case '+': 3019 if (Rc.mode_altscr) 3020 SETw(Curwin, EQUWINS_cwo); 3021 break; 3022 3023 case '<': 3024 if (VIZCHKc) { 3025 FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1; 3026 while (*p != Curwin->rc.sortindx) 3027 --p; 3028 if (--p >= Curwin->procflags) 3029 Curwin->rc.sortindx = *p; 3030 } 3031 break; 3032 3033 case '>': 3034 if (VIZCHKc) { 3035 FLG_t *p = Curwin->procflags; 3036 while (*p != Curwin->rc.sortindx) 3037 ++p; 3038 if (++p < Curwin->procflags + Curwin->maxpflgs) 3039 Curwin->rc.sortindx = *p; 3040 } 3041 break; 3042 3043 case 'M': // these keys represent old-top compatability 3044 case 'N': // -- grouped here so that if users could ever 3045 case 'P': // be weaned, we would just whack this part... 3046 case 'T': 3047 { 3048 static struct { 3049 const unsigned xkey; 3050 const char *xmsg; 3051 const FLG_t sort; 3052 } xtab[] = { 3053 { 3054 'M', "Memory", P_MEM,}, { 3055 'N', "Numerical", P_PID,}, { 3056 'P', "CPU", P_CPU,}, { 3057 'T', "Time", P_TM2},}; 3058 int i; 3059 for (i = 0; i < MAXTBL(xtab); ++i) 3060 if (c == xtab[i].xkey) { 3061 Curwin->rc.sortindx = xtab[i].sort; 3062 show_msg(fmtmk 3063 ("%s sort compatibility key honored", 3064 xtab[i].xmsg)); 3065 break; 3066 } 3067 } 3068 break; 3069 3070 case '\n': // just ignore these, they'll have the effect 3071 case ' ': // of refreshing display after waking us up ! 3072 break; 3073 3074 default: 3075 show_msg("\aUnknown command - try 'h' for help"); 3076 } 3077 /* The following assignment will force a rebuild of all column headers and 3078 the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are 3079 some keys that COULD require new column headers and/or libproc flags: 3080 'A' - likely 3081 'c' - likely when !Mode_altscr, maybe when Mode_altscr 3082 'F' - maybe, if new field forced on 3083 'f' - likely 3084 'G' - likely 3085 'O' - maybe, if new field forced on 3086 'o' - maybe, if new field brought into view 3087 'Z' - likely, if 'Curwin' changed when !Mode_altscr 3088 '-' - likely (restricted to Mode_altscr) 3089 '_' - likely (restricted to Mode_altscr) 3090 '=' - maybe, but only when Mode_altscr 3091 '+' - likely (restricted to Mode_altscr) 3092 ( At this point we have a human being involved and so have all the time ) 3093 ( in the world. We can afford a few extra cpu cycles every now & then! ) 3094 */ 3095 Frames_libflags = 0; 3096} 3097 3098 /* 3099 * State display *Helper* function to calc and display the state 3100 * percentages for a single cpu. In this way, we can support 3101 * the following environments without the usual code bloat. 3102 * 1) single cpu machines 3103 * 2) modest smp boxes with room for each cpu's percentages 3104 * 3) massive smp guys leaving little or no room for process 3105 * display and thus requiring the cpu summary toggle */ 3106static void summaryhlp(CPU_t * cpu, const char *pfx) 3107{ 3108 /* we'll trim to zero if we get negative time ticks, 3109 which has happened with some SMP kernels (pre-2.4?) */ 3110#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz) 3111 SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, tot_frme, tz; 3112 float scale; 3113 3114 u_frme = cpu->u - cpu->u_sav; 3115 s_frme = cpu->s - cpu->s_sav; 3116 n_frme = cpu->n - cpu->n_sav; 3117 i_frme = TRIMz(cpu->i - cpu->i_sav); 3118 w_frme = cpu->w - cpu->w_sav; 3119 tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme; 3120 if (1 > tot_frme) 3121 tot_frme = 1; 3122 scale = 100.0 / (float)tot_frme; 3123 3124 /* display some kinda' cpu state percentages 3125 (who or what is explained by the passed prefix) */ 3126 show_special(0, 3127 fmtmk(States_fmts, pfx, (float)u_frme * scale, 3128 (float)s_frme * scale, (float)n_frme * scale, 3129 (float)i_frme * scale, (float)w_frme * scale)); 3130 if (o_flag) 3131 fprintf(outfile, 3132 " %s User:%.2f\tSystem:%.2f\tNice:%.2f\t\tIdle:%.2f\tIO-wait:%.2f\n", 3133 pfx, (float)u_frme * scale, (float)s_frme * scale, 3134 (float)n_frme * scale, (float)i_frme * scale, 3135 (float)w_frme * scale); 3136 Msg_row += 1; 3137 3138 // remember for next time around 3139 cpu->u_sav = cpu->u; 3140 cpu->s_sav = cpu->s; 3141 cpu->n_sav = cpu->n; 3142 cpu->i_sav = cpu->i; 3143 cpu->w_sav = cpu->w; 3144 3145#undef TRIMz 3146} 3147 3148 /* 3149 * Begin a new frame by: 3150 * 1) Refreshing the all important proc table 3151 * 2) Displaying uptime and load average (maybe) 3152 * 3) Displaying task/cpu states (maybe) 3153 * 4) Displaying memory & swap usage (maybe) 3154 * and then, returning a pointer to the pointers to the proc_t's! */ 3155static proc_t **summary_show(void) 3156{ 3157 static proc_t **p_table = NULL; 3158 static CPU_t *smpcpu = NULL; 3159 3160 // whoa first time, gotta' prime the pump... 3161 if (!p_table) { 3162 p_table = procs_refresh(NULL, L_DEFAULT); 3163 putp(Cap_clr_scr); 3164 sleep(1); 3165 } else 3166 putp(Batch ? "\n\n" : Cap_home); 3167 p_table = procs_refresh(p_table, Frames_libflags); 3168 3169 /* 3170 ** Display Uptime and Loadavg */ 3171 if (CHKw(Curwin, View_LOADAV)) { 3172 if (!Rc.mode_altscr) 3173 show_special(0, 3174 fmtmk(LOADAV_line, Myname, 3175 sprint_uptime())); 3176 else 3177 show_special(0, fmtmk(CHKw(Curwin, VISIBLE_tsk) 3178 ? LOADAV_line_alt 3179 : LOADAV_line, Curwin->grpname, 3180 sprint_uptime())); 3181 Msg_row += 1; 3182 } 3183 3184 /* 3185 ** Display Task and Cpu(s) States */ 3186 3187 if (CHKw(Curwin, View_STATES)) { 3188 show_special(0, 3189 fmtmk(STATES_line1, Frame_maxtask, Frame_running, 3190 Frame_sleepin, Frame_stopped, 3191 Frame_zombied)); 3192 if (o_flag) 3193 fprintf(outfile, 3194 " MaxTasks:%d\t\tRunningTasks:%d\tSleepingTasks:%d\tStoppedTasks:%d\tZombieTasks:%d\n\n", 3195 Frame_maxtask, Frame_running, Frame_sleepin, 3196 Frame_stopped, Frame_zombied); 3197 Msg_row += 1; 3198 3199 smpcpu = cpus_refresh(smpcpu); 3200 3201 if (CHKw(Curwin, View_CPUSUM)) { 3202 // display just the 1st /proc/stat line 3203 summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); 3204 if (o_flag) 3205 fprintf(outfile, "\n"); 3206 } else { 3207 int i; 3208 char tmp[SMLBUFSIZ]; 3209 // display each cpu's states separately 3210 for (i = 0; i < Cpu_tot; i++) { 3211 snprintf(tmp, sizeof(tmp), " Cpu%-2d:", 3212 Rc.mode_irixps ? i : Cpu_map[i]); 3213 summaryhlp(&smpcpu[i], tmp); 3214 } 3215 } 3216 } 3217 3218 /* 3219 ** Display Memory and Swap stats */ 3220 meminfo(); 3221 if (CHKw(Curwin, View_MEMORY)) { 3222 show_special(0, 3223 fmtmk(MEMORY_line1, kb_main_total, kb_main_used, 3224 kb_main_free, kb_main_buffers)); 3225 if (o_flag) 3226 fprintf(outfile, 3227 " TotalMem:%dk\tUsedMem:%dk\tFreeMem:%dk\t\tBuffers:%dk\n", 3228 kb_main_total, kb_main_used, kb_main_free, 3229 kb_main_buffers); 3230 show_special(0, 3231 fmtmk(MEMORY_line2, kb_swap_total, kb_swap_used, 3232 kb_swap_free, kb_main_cached)); 3233 if (o_flag) 3234 fprintf(outfile, 3235 " TotalSwap:%dk\tUsedSwap:%dk\tFreeSwap:%dk\tCached:%dk\n", 3236 kb_swap_total, kb_swap_used, kb_swap_free, 3237 kb_main_cached); 3238 Msg_row += 2; 3239 } 3240 if (o_flag) 3241 fprintf(outfile, "============\n"); 3242 SETw(Curwin, NEWFRAM_cwo); 3243 return p_table; 3244} 3245 3246 /* 3247 * Display information for a single task row. */ 3248static void task_show(const WIN_t * q, const proc_t * p) 3249{ 3250 /* the following macro is our means to 'inline' emitting a column -- next to 3251 procs_refresh, that's the most frequent and costly part of top's job ! */ 3252#define MKCOL(va...) do { \ 3253 if (likely(!(CHKw(q, Show_HICOLS) && q->rc.sortindx == i))) \ 3254 snprintf(cbuf, sizeof(cbuf), f, ## va); \ 3255 else { \ 3256 snprintf(_z, sizeof(_z), f, ## va); \ 3257 snprintf(cbuf, sizeof(cbuf), "%s%s%s", q->capclr_rowhigh, _z \ 3258 , !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : ""); \ 3259 pad += q->len_rowhigh; \ 3260 if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \ 3261 } } while (0) 3262 char rbuf[ROWBUFSIZ], *rp; 3263 int j, x, pad; 3264 3265 // we must begin a row with a possible window number in mind... 3266 *(rp = rbuf) = '\0'; 3267 if ((pad = Rc.mode_altscr)) 3268 rp = scat(rp, " "); 3269 3270 for (x = 0; x < q->maxpflgs; x++) { 3271 char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ]; 3272 FLG_t i = q->procflags[x]; // support for our field/column 3273 const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt 3274 unsigned s = Fieldstab[i].scale; // string must be altered ! 3275 unsigned w = Fieldstab[i].width; 3276 3277 switch (i) { 3278 case P_CMD: 3279 { 3280 char tmp[ROWBUFSIZ]; 3281 unsigned flags; 3282 if (CHKw(q, Show_CMDLIN)) 3283 flags = 3284 ESC_DEFUNCT | ESC_BRACKETS | 3285 ESC_ARGS; 3286 else 3287 flags = ESC_DEFUNCT; 3288 escape_command(tmp, p, sizeof tmp, q->maxcmdln, 3289 flags); 3290 MKCOL(q->maxcmdln, q->maxcmdln, tmp); 3291 } 3292 break; 3293 case P_COD: 3294 MKCOL(scale_num(PAGES_2K(p->trs), w, s)); 3295 break; 3296 case P_CPN: 3297 MKCOL((unsigned)p->processor); 3298 break; 3299 case P_CPU: 3300 { 3301 float u = (float)p->pcpu * Frame_tscale; 3302 if (99.9 < u) 3303 u = 99.9; 3304 MKCOL(u); 3305 } 3306 break; 3307 case P_DAT: 3308 MKCOL(scale_num(PAGES_2K(p->drs), w, s)); 3309 break; 3310 case P_DRT: 3311 MKCOL(scale_num((unsigned)p->dt, w, s)); 3312 break; 3313 case P_FLG: 3314 { 3315 char tmp[TNYBUFSIZ]; 3316 snprintf(tmp, sizeof(tmp), f, (long)p->flags); 3317 for (j = 0; tmp[j]; j++) 3318 if ('0' == tmp[j]) 3319 tmp[j] = '.'; 3320 f = tmp; 3321 MKCOL(""); 3322 } 3323 break; 3324 case P_FLT: 3325 MKCOL(scale_num(p->maj_flt, w, s)); 3326 break; 3327 case P_GRP: 3328 MKCOL(p->egroup); 3329 break; 3330 case P_MEM: 3331 MKCOL((float)PAGES_2K(p->resident) * 100 / 3332 kb_main_total); 3333 break; 3334 case P_NCE: 3335 MKCOL((int)p->nice); 3336 break; 3337 case P_PID: 3338 MKCOL((unsigned)p->pid); 3339 break; 3340 case P_PPD: 3341 MKCOL((unsigned)p->ppid); 3342 break; 3343 case P_PRI: 3344 if (unlikely(-99 > p->priority) 3345 || unlikely(999 < p->priority)) { 3346 f = " RT "; 3347 MKCOL(""); 3348 } else 3349 MKCOL((int)p->priority); 3350 break; 3351 case P_RES: 3352 MKCOL(scale_num(PAGES_2K(p->resident), w, s)); 3353 break; 3354 case P_SHR: 3355 MKCOL(scale_num(PAGES_2K(p->share), w, s)); 3356 break; 3357 case P_STA: 3358#ifdef USE_LIB_STA3 3359 MKCOL(status(p)); 3360#else 3361 MKCOL(p->state); 3362#endif 3363 break; 3364 case P_SWP: 3365 MKCOL(scale_num(PAGES_2K(p->size - p->resident), w, s)); 3366 break; 3367 case P_TME: 3368 case P_TM2: 3369 { 3370 TIC_t t = p->utime + p->stime; 3371 if (CHKw(q, Show_CTIMES)) 3372 t += (p->cutime + p->cstime); 3373 MKCOL(scale_tics(t, w)); 3374 } 3375 break; 3376 case P_TTY: 3377 { 3378 char tmp[TNYBUFSIZ]; 3379 dev_to_tty(tmp, (int)w, p->tty, p->pid, 3380 ABBREV_DEV); 3381 MKCOL(tmp); 3382 } 3383 break; 3384 case P_UID: 3385 MKCOL((unsigned)p->euid); 3386 break; 3387 case P_URE: 3388 MKCOL(p->euser); 3389 break; 3390 case P_URR: 3391 MKCOL(p->ruser); 3392 break; 3393 case P_VRT: 3394 MKCOL(scale_num(PAGES_2K(p->size), w, s)); 3395 break; 3396 case P_WCH: 3397 if (No_ksyms) { 3398#ifdef CASEUP_HEXES 3399 f = "%08lX "; 3400#else 3401 f = "%08lx "; 3402#endif 3403 MKCOL((long)p->wchan); 3404 } else { 3405 MKCOL(wchan(p->wchan, p->pid)); 3406 } 3407 break; 3408 3409 } /* end: switch 'procflag' */ 3410 3411 rp = scat(rp, cbuf); 3412 } /* end: for 'maxpflgs' */ 3413 3414 PUFF("\n%s%.*s%s%s", (CHKw(q, Show_HIROWS) && 'R' == p->state) 3415 ? q->capclr_rowhigh : q->capclr_rownorm, Screen_cols + pad, rbuf, 3416 Caps_off, Cap_clr_eol); 3417 3418#undef MKCOL 3419} 3420 3421 /* 3422 * Squeeze as many tasks as we can into a single window, 3423 * after sorting the passed proc table. */ 3424static void window_show(proc_t ** ppt, WIN_t * q, int *lscr) 3425{ 3426#ifdef SORT_SUPRESS 3427 // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort 3428#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES ) 3429 static FLG_t sav_indx = 0; 3430 static int sav_flgs = -1; 3431#endif 3432 int i, lwin; 3433 3434 /* 3435 ** Display Column Headings -- and distract 'em while we sort (maybe) */ 3436 PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol); 3437 3438#ifdef SORT_SUPRESS 3439 if (CHKw(Curwin, NEWFRAM_cwo) 3440 || sav_indx != q->rc.sortindx 3441 || sav_flgs != (q->rc.winflags & srtMASK)) { 3442 sav_indx = q->rc.sortindx; 3443 sav_flgs = (q->rc.winflags & srtMASK); 3444#endif 3445 if (CHKw(q, Qsrt_NORMAL)) 3446 Frame_srtflg = 1; // this one's always needed! 3447 else 3448 Frame_srtflg = -1; 3449 Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe 3450 Frame_cmdlin = CHKw(q, Show_CMDLIN); 3451 qsort(ppt, Frame_maxtask, sizeof(proc_t *), 3452 Fieldstab[q->rc.sortindx].sort); 3453#ifdef SORT_SUPRESS 3454 } 3455#endif 3456 // account for column headings 3457 (*lscr)++; 3458 lwin = 1; 3459 i = 0; 3460 3461 while (-1 != ppt[i]->pid && *lscr < Max_lines 3462 && (!q->winlines || (lwin <= q->winlines))) { 3463 if ((CHKw(q, Show_IDLEPS) 3464 || ('S' != ppt[i]->state && 'Z' != ppt[i]->state)) 3465 && good_uid(ppt[i])) { 3466 /* 3467 ** Display a process Row */ 3468 task_show(q, ppt[i]); 3469 (*lscr)++; 3470 ++lwin; 3471 } 3472 ++i; 3473 } 3474 // for this frame that window's toast, cleanup for next time 3475 q->winlines = 0; 3476 OFFw(Curwin, FLGSOFF_cwo); 3477 3478#ifdef SORT_SUPRESS 3479#undef srtMASK 3480#endif 3481} 3482 3483/*###### Entry point plus two ##########################################*/ 3484 3485 /* 3486 * This guy's just a *Helper* function who apportions the 3487 * remaining amount of screen real estate under multiple windows */ 3488static void framehlp(int wix, int max) 3489{ 3490 int i, rsvd, size, wins; 3491 3492 // calc remaining number of visible windows + total 'user' lines 3493 for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) { 3494 if (CHKw(&Winstk[i], VISIBLE_tsk)) { 3495 rsvd += Winstk[i].rc.maxtasks; 3496 ++wins; 3497 if (max <= rsvd) 3498 break; 3499 } 3500 } 3501 if (!wins) 3502 wins = 1; 3503 // set aside 'rsvd' & deduct 1 line/window for the columns heading 3504 size = (max - wins) - rsvd; 3505 if (0 <= size) 3506 size = max; 3507 size = (max - wins) / wins; 3508 3509 /* for remaining windows, set WIN_t winlines to either the user's 3510 maxtask (1st choice) or our 'foxized' size calculation 3511 (foxized adj. - 'fair and balanced') */ 3512 for (i = wix; i < GROUPSMAX; i++) { 3513 if (CHKw(&Winstk[i], VISIBLE_tsk)) { 3514 Winstk[i].winlines = 3515 Winstk[i].rc.maxtasks ? Winstk[i].rc. 3516 maxtasks : size; 3517 } 3518 } 3519} 3520 3521 /* 3522 * Initiate the Frame Display Update cycle at someone's whim! 3523 * This routine doesn't do much, mostly he just calls others. 3524 * 3525 * (Whoa, wait a minute, we DO caretake those row guys, plus) 3526 * (we CALCULATE that IMPORTANT Max_lines thingy so that the) 3527 * (*subordinate* functions invoked know WHEN the user's had) 3528 * (ENOUGH already. And at Frame End, it SHOULD be apparent) 3529 * (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) 3530 * (the CURSOR is STUCK in just the RIGHT place, know what I) 3531 * (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) 3532 * (THAT about THIS function again, Ok? Good that's better.) 3533 * 3534 * (ps. we ARE the UNEQUALED justification KING of COMMENTS!) 3535 * (No, I don't mean significance/relevance, only alignment.) 3536 * 3537 * (What's that? Are you sure? Old main's REALLY GOOD too?) 3538 * (You say he even JUSTIFIES comments in his FUNCTION BODY?) 3539 * (Jeeze, how COULD I have known? That sob's NOT IN SCOPE!) 3540 */ 3541static void frame_make(void) 3542{ 3543 proc_t **ppt; 3544 int i, scrlins; 3545 3546 /* note: except for PROC_PID, all libproc flags are managed by 3547 reframewins(), who also builds each window's column headers */ 3548 if (!Frames_libflags) { 3549 reframewins(); 3550 memset(Pseudo_scrn, '\0', Pseudo_size); 3551 } 3552 Pseudo_row = Msg_row = scrlins = 0; 3553 ppt = summary_show(); 3554 Max_lines = (Screen_rows - Msg_row) - 1; 3555 3556 if (CHKw(Curwin, EQUWINS_cwo)) 3557 wins_reflag(Flags_OFF, EQUWINS_cwo); 3558 3559 // sure hope each window's columns header begins with a newline... 3560 putp(tg2(0, Msg_row)); 3561 3562 if (!Rc.mode_altscr) { 3563 // only 1 window to show so, piece o' cake 3564 Curwin->winlines = Curwin->rc.maxtasks; 3565 window_show(ppt, Curwin, &scrlins); 3566 } else { 3567 // maybe NO window is visible but assume, pieces o' cakes 3568 for (i = 0; i < GROUPSMAX; i++) { 3569 if (CHKw(&Winstk[i], VISIBLE_tsk)) { 3570 framehlp(i, Max_lines - scrlins); 3571 window_show(ppt, &Winstk[i], &scrlins); 3572 } 3573 if (Max_lines <= scrlins) 3574 break; 3575 } 3576 } 3577 /* clear to end-of-screen (critical if last window is 'idleps off'), 3578 then put the cursor in-its-place, and rid us of any prior frame's msg 3579 (main loop must iterate such that we're always called before sleep) */ 3580 PUTT("%s%s%s%s", scrlins < Max_lines ? "\n" : "", 3581 scrlins < Max_lines ? Cap_clr_eos : "", tg2(0, Msg_row) 3582 , Cap_clr_eol); 3583 fflush(stdout); 3584} 3585 3586 /* 3587 * Darling, you DO look simply MARVELOUS -- have you been dieting? 3588 * Or maybe it's because YOU and WINDOWS seem such a HAPPY couple. 3589 * 3590 * Of course NO. Not THOSE deathly BLUE WINDOWS! I mean your 'A' 3591 * mode (alt display) windows. Yes, yes those completely OPTIONAL 3592 * ones. We ALL know you'd NEVER FORCE that interface on ANY user 3593 * - unlike You-Know-Who! Well I've got to run. But you're doing 3594 * it just SPLENDIDLY! You go right on doing it EXACTLY the SAME! 3595 */ 3596int main(int dont_care_argc, char **argv) 3597{ 3598 (void)dont_care_argc; 3599 before(*argv); 3600 /* 3601 Ok, she's gone now. Don't you mind her, she means well but yes, she is 3602 a bit of a busy-body. Always playing the matchmaker role, trying to do 3603 away with unmarried windows and bachelors. So, back to business buddy! 3604 3605 You're hungry, you said? How'd you like a sandwich? No, no, no -- not 3606 the usual slopped together, hacked up illogic. I'm talkin' a carefully 3607 reasoned, artfully crafted, extremely capable, well behaved executable! 3608 3609 Well then, here, try THIS sandwich: */ 3610 // +-------------+ 3611 windows_stage1(); // top (sic) slice 3612 configs_read(); // > spread etc, < 3613 parse_args(&argv[1]); // > lean stuff, < 3614 whack_terminal(); // > onions etc. < 3615 windows_stage2(); // as bottom slice 3616 // +-------------+ 3617 signal(SIGALRM, end_pgm); 3618 signal(SIGHUP, end_pgm); 3619 signal(SIGINT, end_pgm); 3620 signal(SIGPIPE, end_pgm); 3621 signal(SIGQUIT, end_pgm); 3622 signal(SIGTERM, end_pgm); 3623 signal(SIGTSTP, suspend); 3624 signal(SIGTTIN, suspend); 3625 signal(SIGTTOU, suspend); 3626 signal(SIGCONT, wins_resize); 3627 signal(SIGWINCH, wins_resize); 3628 3629 for (;;) { 3630 struct timeval tv; 3631 fd_set fs; 3632 char c; 3633 // This is it? 3634 frame_make(); // Impossible! 3635 3636 if (Msg_awaiting) 3637 show_msg(Msg_delayed); 3638 if (0 < Loops) 3639 --Loops; 3640 if (!Loops) 3641 end_pgm(0); 3642 3643 if (Batch) 3644 sleep((unsigned)Rc.delay_time); 3645 else { // Linux reports time not slept, 3646 tv.tv_sec = Rc.delay_time; // so we must reinit every time. 3647 tv.tv_usec = 3648 (Rc.delay_time - (int)Rc.delay_time) * 1000000; 3649 FD_ZERO(&fs); 3650 FD_SET(STDIN_FILENO, &fs); 3651 if (0 < select(STDIN_FILENO + 1, &fs, NULL, NULL, &tv) 3652 && 0 < chin(0, &c, 1)) 3653 do_key((unsigned)c); 3654 } 3655 } 3656 3657 /* 3658 (listen before we return, aren't you sort of sad for old 'frame_make'?) 3659 (so, uh, why don't we just move this main guy to near the beginning of) 3660 (the C source file. then that poor old function would be sure to have) 3661 (at least a chance at scopin' us out, ya know what i mean? so what do) 3662 (ya think? all things considered, would that be a proper thing to do?) 3663 3664 Now there's an EXCELLENT idea! After all, SOME programmers DO code the 3665 main function ANY OLD PLACE they feel like. And in doing THAT, they're 3666 helping to keep those that FOLLOW out of mischief, busy HUNTING for the 3667 <bleepin'> thing. Further, by moving it we can contribute to PROTOTYPE 3668 PROLIFERATION for every function main calls. Don't you KNOW that those 3669 declarations OFTEN LINGER, usually long AFTER the REAL definitions have 3670 DISAPPEARED, since programs do evolve? Yep that's a GREAT idea you got 3671 there, NICE GOING! But, here's an opposing view: ANYONE who'd put main 3672 ANYWHERE such that its LOCATION cannot be REACHED with JUST 1 KEYSTROKE 3673 better turn in their Coding_Badge and Pocket_Protector then -- BE GONE! 3674 The main function has only ONE proper HOME: always the LAST function in 3675 that C Listing. End-of-Story, No-More-Discussion, so BE QUIET already! 3676 3677 \---------------------------------------------------------------------/ 3678 Sheeesh, didn't that doofus know the return statement can't be executed 3679 or that we end via a caught signal? Oh Lordy, I is DROWNING in morons! 3680 They done REACHED clear up to my OUTER braces! We's surely DOOMED now! 3681 /---------------------------------------------------------------------\ 3682 */ 3683 return 0; 3684} 3685