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. */
59 static FILE *outfile;
60 static FILE *datafile;
61 static int o_flag;
62 /* The original and new terminal attributes */
63 static struct termios Savedtty, Rawtty;
64 static int Ttychanged = 0;
65
66 /* Program name used in error messages and local 'rc' file name */
67 static 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 */
72 static char Rc_name[OURPATHSZ];
73 static RCF_t Rc = DEF_RCFILE;
74
75 /* The run-time acquired page size */
76 static int Page_size;
77
78 /* SMP, Irix/Solaris mode, Linux 2.5.xx support */
79 static int Cpu_tot, *Cpu_map;
80 /* assume no IO-wait stats, overridden if linux 2.5.41 */
81 static const char *States_fmts = STATES_line2x4;
82
83 /* Specific process id monitoring support */
84 static pid_t Monpids[MONPIDMAX] = { 0 };
85
86 static int Monpidsidx = 0;
87
88 /* A postponed error message */
89 static char Msg_delayed[SMLBUFSIZ];
90 static 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. */
99 static 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. */
104 static int Msg_row;
105
106 /* Global/Non-windows mode stuff that is NOT persistent */
107 static 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 */
115 static 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] = "";
123 static 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. */
130 static char *Pseudo_scrn;
131 static int Pseudo_row, Pseudo_cols, Pseudo_size;
132 #ifndef STDOUT_IOLBF
133 // less than stdout's normal buffer but with luck mostly '\n' anyway
134 static 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) */
143 static 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) */
149 static int Frames_libflags; // PROC_FILLxxx flags (0 = need new)
150 //atic int Frames_maxcmdln; // the largest from the 4 windows
151 static unsigned Frame_maxtask; // last known number of active tasks
152 // ie. current 'size' of proc table
153 static unsigned Frame_running, // state categories for this frame
154 Frame_sleepin, Frame_stopped, Frame_zombied;
155 static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu'
156 static 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
SCB_NUMx(P_PID,pid)169 SCB_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 !
181 static 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
SCB_NUM1(P_VRT,size)199 SCB_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
209 static 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
SCB_NUM1(P_WCH,wchan)226 SCB_NUM1(P_WCH, wchan)
227 SCB_NUM1(P_FLG, flags)
228
229 /* ///////////////////////////////// special sort for prochlp() ! */
230 static 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 */
chin(int ech,char * buf,unsigned cnt)240 static 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... */
fmtmk(const char * fmts,...)260 static 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) */
scat(char * restrict dst,const char * restrict src)274 static 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')
strim_0(char * str)284 static 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. */
tg2(int x,int y)298 static 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. */
308 static void bye_bye(int eno, const char *str) NORETURN;
bye_bye(int eno,const char * str)309 static 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 */
385 static void end_pgm(int dont_care_sig) NORETURN;
end_pgm(int dont_care_sig)386 static 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 */
394 static void std_err(const char *str) NORETURN;
std_err(const char * str)395 static 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 */
suspend(int dont_care_sig)416 static 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 */
capsmk(WIN_t * q)438 static 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) */
msg_save(const char * fmts,...)504 static 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) */
show_msg(const char * str)520 static 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 */
show_pmt(const char * str)531 static 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) */
show_special(int interact,const char * glob)563 static 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 */
ask4str(const char * prompt)629 static 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 */
get_float(const char * prompt)642 static 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 */
get_int(const char * prompt)660 static 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 */
scale_num(unsigned num,const int width,const unsigned type)684 static 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'. */
scale_tics(TIC_t tics,const int width)720 static 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
768 static int selection_type;
769 static uid_t selection_uid;
770
771 // FIXME: this is "temporary" code we hope
good_uid(const proc_t * restrict const pp)772 static 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
parse_uid(const char * restrict const str,uid_t * restrict const ret)798 static 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
825 static void *alloc_c(unsigned numb) MALLOC;
alloc_c(unsigned numb)826 static 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
837 static void *alloc_r(void *q, unsigned numb) MALLOC;
alloc_r(void * q,unsigned numb)838 static 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 */
cpus_refresh(CPU_t * cpus)855 static 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 */
prochlp(proc_t * this)910 static 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> */
procs_refresh(proc_t ** table,int flags)1017 static 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. */
1096 static 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
1189 static 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
ft_get_char(const int fr,int i)1204 static 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
1219 static 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
ft_get_ptr(const int fr,int c)1234 static 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
1249 static 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
1259 static 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
1272 static 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 */
rc_read_new(const char * const buf,RCF_t * rc)1299 static 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
rc_read_old(const char * const buf,RCF_t * rc)1366 static 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
rc_write_new(FILE * fp)1500 static 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
1541 static 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
rc_write_whatever(void)1625 static 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! */
before(char * me)1646 static 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) */
confighlp(char * fields)1678 static 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. */
configs_read(void)1742 static 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
parse_args(char ** args)1820 static 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 */
whack_terminal(void)2122 static 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 */
display_fields(const char * fields,const char * xtra)2166 static 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. */
fields_reorder(void)2206 static 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. */
fields_sort(void)2241 static 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. */
fields_toggle(void)2275 static 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! */
reframewins(void)2310 static 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. */
win_names(WIN_t * q,const char * name)2406 static 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"). */
win_select(char ch)2414 static 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. */
win_warn(void)2442 static 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. */
winsclrhlp(WIN_t * q,int save)2455 static 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 */
wins_colors(void)2478 static 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. */
wins_reflag(int what,int flg)2558 static 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 */
wins_resize(int dont_care_sig)2589 static 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 --- ] */
windows_stage1(void)2628 static 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 */
windows_stage2(void)2659 static 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 */
do_key(unsigned c)2675 static 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 */
summaryhlp(CPU_t * cpu,const char * pfx)3106 static 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! */
summary_show(void)3155 static 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. */
task_show(const WIN_t * q,const proc_t * p)3248 static 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. */
window_show(proc_t ** ppt,WIN_t * q,int * lscr)3424 static 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 */
framehlp(int wix,int max)3488 static 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 */
frame_make(void)3541 static 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 */
main(int dont_care_argc,char ** argv)3596 int 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