1
/* top.c - Source file: show Linux processes */
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>
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.
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>
20
* Changes by Albert Cahalan, 2002-2004.
22
#include <sys/ioctl.h>
23
#include <sys/resource.h>
25
#include <sys/types.h>
37
// Foul POS defines all sorts of stuff...
46
#include "proc/devname.h"
47
#include "proc/wchan.h"
48
#include "proc/procps.h"
49
#include "proc/readproc.h"
50
#include "proc/escape.h"
52
#include "proc/sysinfo.h"
53
#include "proc/version.h"
54
#include "proc/whattime.h"
58
/*###### Miscellaneous global stuff ####################################*/
60
/* The original and new terminal attributes */
61
static struct termios Savedtty,
63
static int Ttychanged = 0;
65
/* Program name used in error messages and local 'rc' file name */
68
/* Name of user config file (dynamically constructed) and our
69
'Current' rcfile contents, initialized with defaults but may be
70
overridden with the local rcfile (old or new-style) values */
71
static char Rc_name [OURPATHSZ];
72
static RCF_t Rc = DEF_RCFILE;
74
/* The run-time acquired page size */
75
static unsigned Page_size;
76
static unsigned page_to_kb_shift;
78
/* SMP Irix/Solaris mode */
80
static double pcpu_max_value; // usually 99.9, for %CPU display
81
/* assume no IO-wait stats, overridden if linux 2.5.41 */
82
static const char *States_fmts = STATES_line2x4;
84
/* Specific process id monitoring support */
85
static pid_t Monpids [MONPIDMAX] = { 0 };
86
static int Monpidsidx = 0;
88
/* A postponed error message */
89
static char Msg_delayed [SMLBUFSIZ];
90
static int Msg_awaiting = 0;
92
// This is the select() timeout. Clear it in sig handlers to avoid a race.
93
// (signal happens just as we are about to select() and thus does not
94
// break us out of the select(), causing us to delay until timeout)
95
static volatile struct timeval tv;
96
#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0);
98
/* Configurable Display support ##################################*/
100
/* Current screen dimensions.
101
note: the number of processes displayed is tracked on a per window
102
basis (see the WIN_t). Max_lines is the total number of
103
screen rows after deducting summary information overhead. */
104
/* Current terminal screen size. */
105
static int Screen_cols, Screen_rows, Max_lines;
107
// set to 1 if writing to the last column would be troublesome
108
// (we don't distinguish the lowermost row from the other rows)
109
static int avoid_last_column;
111
/* This is really the number of lines needed to display the summary
112
information (0 - nn), but is used as the relative row where we
113
stick the cursor between frames. */
116
/* Global/Non-windows mode stuff that is NOT persistent */
117
static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise
118
PSDBopen = 0, // set to '1' if psdb opened (now postponed)
119
Batch = 0, // batch mode, collect no input, dumb output
120
Loops = -1, // number of iterations, -1 loops forever
121
Secure_mode = 0; // set if some functionality restricted
123
/* Some cap's stuff to reduce runtime calls --
124
to accomodate 'Batch' mode, they begin life as empty strings */
125
static char Cap_clr_eol [CAPBUFSIZ],
126
Cap_clr_eos [CAPBUFSIZ],
127
Cap_clr_scr [CAPBUFSIZ],
128
Cap_rmam [CAPBUFSIZ],
129
Cap_smam [CAPBUFSIZ],
130
Cap_curs_norm [CAPBUFSIZ],
131
Cap_curs_huge [CAPBUFSIZ],
132
Cap_home [CAPBUFSIZ],
133
Cap_norm [CAPBUFSIZ],
134
Cap_reverse [CAPBUFSIZ],
135
Caps_off [CAPBUFSIZ];
136
static int Cap_can_goto = 0;
138
/* Some optimization stuff, to reduce output demands...
139
The Pseudo_ guys are managed by wins_resize and frame_make. They
140
are exploited in a macro and represent 90% of our optimization.
141
The Stdout_buf is transparent to our code and regardless of whose
142
buffer is used, stdout is flushed at frame end or if interactive. */
143
static char *Pseudo_scrn;
144
static int Pseudo_row, Pseudo_cols, Pseudo_size;
146
// less than stdout's normal buffer but with luck mostly '\n' anyway
147
static char Stdout_buf[2048];
151
/* ////////////////////////////////////////////////////////////// */
152
/* Special Section: multiple windows/field groups ---------------*/
154
/* The pointers to our four WIN_t's, and which of those is considered
155
the 'current' window (ie. which window is associated with any summ
156
info displayed and to which window commands are directed) */
157
static WIN_t Winstk [GROUPSMAX],
160
/* Frame oriented stuff that can't remain local to any 1 function
161
and/or that would be too cumbersome managed as parms,
162
and/or that are simply more efficiently handled as globals
163
(first 2 persist beyond a single frame, changed infrequently) */
164
static int Frames_libflags; // PROC_FILLxxx flags (0 = need new)
165
//atic int Frames_maxcmdln; // the largest from the 4 windows
166
static unsigned Frame_maxtask; // last known number of active tasks
167
// ie. current 'size' of proc table
168
static unsigned Frame_running, // state categories for this frame
172
static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu'
173
static int Frame_srtflg, // the subject window's sort direction
174
Frame_ctimes, // the subject window's ctimes flag
175
Frame_cmdlin; // the subject window's cmdlin flag
176
/* ////////////////////////////////////////////////////////////// */
179
/*###### Sort callbacks ################################################*/
182
* These happen to be coded in the same order as the enum 'pflag'
183
* values. Note that 2 of these routines serve double duty --
187
SCB_NUMx(P_PID, XXXID)
188
SCB_NUMx(P_PPD, ppid)
189
SCB_STRx(P_URR, ruser)
190
SCB_NUMx(P_UID, euid)
191
SCB_STRx(P_URE, euser)
192
SCB_STRx(P_GRP, egroup)
194
SCB_NUMx(P_PRI, priority)
195
SCB_NUMx(P_NCE, nice)
196
SCB_NUMx(P_CPN, processor)
197
SCB_NUM1(P_CPU, pcpu)
198
// also serves P_TM2 !
199
static int sort_P_TME (const proc_t **P, const proc_t **Q)
202
if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
203
< ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
205
if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
206
> ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
209
if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
211
if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
217
SCB_NUM1(P_VRT, size)
218
SCB_NUM2(P_SWP, size, resident)
219
SCB_NUM1(P_RES, resident) // also serves P_MEM !
222
SCB_NUM1(P_SHR, share)
223
SCB_NUM1(P_FLT, maj_flt)
225
SCB_NUMx(P_STA, state)
227
static int sort_P_CMD (const proc_t **P, const proc_t **Q)
229
/* if a process doesn't have a cmdline, we'll consider it a kernel thread
230
-- since displayed tasks are given special treatment, we must too */
231
if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
232
if (!(*Q)->cmdline) return Frame_srtflg * -1;
233
if (!(*P)->cmdline) return Frame_srtflg;
234
return Frame_srtflg *
235
strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln);
237
// this part also handles the compare if both are kernel threads
238
return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
241
SCB_NUM1(P_WCH, wchan)
242
SCB_NUM1(P_FLG, flags)
244
/* ///////////////////////////////// special sort for prochlp() ! */
245
static int sort_HST_t (const HST_t *P, const HST_t *Q)
247
return P->pid - Q->pid;
251
/*###### Tiny useful routine(s) ########################################*/
254
* This routine isolates ALL user INPUT and ensures that we
255
* wont be mixing I/O from stdio and low-level read() requests */
256
static int chin (int ech, char *buf, unsigned cnt)
262
rc = read(STDIN_FILENO, buf, cnt);
264
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
265
rc = read(STDIN_FILENO, buf, cnt);
266
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
268
// may be the beginning of a lengthy escape sequence
269
tcflush(STDIN_FILENO, TCIFLUSH);
270
return rc; // note: we do NOT produce a vaid 'string'
274
// This routine simply formats whatever the caller wants and
275
// returns a pointer to the resulting 'const char' string...
276
static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2)));
277
static const char *fmtmk (const char *fmts, ...)
279
static char buf[BIGBUFSIZ]; // with help stuff, our buffer
280
va_list va; // requirements exceed 1k
283
vsnprintf(buf, sizeof(buf), fmts, va);
285
return (const char *)buf;
289
// This guy is just our way of avoiding the overhead of the standard
290
// strcat function (should the caller choose to participate)
291
static inline char *scat (char *restrict dst, const char *restrict src)
294
while ((*(dst++) = *(src++)));
299
// Trim the rc file lines and any 'open_psdb_message' result which arrives
300
// with an inappropriate newline (thanks to 'sysmap_mmap')
301
static char *strim_0 (char *str)
303
static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape
306
if ((p = strpbrk(str, ws))) *p = 0;
311
// This guy just facilitates Batch and protects against dumb ttys
312
// -- we'd 'inline' him but he's only called twice per frame,
313
// yet used in many other locations.
314
static const char *tg2 (int x, int y)
316
return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
320
/*###### Exit/Interrput routines #######################################*/
322
// The usual program end -- called only by functions in this section.
323
static void bye_bye (FILE *fp, int eno, const char *str) NORETURN;
324
static void bye_bye (FILE *fp, int eno, const char *str)
327
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
328
putp(tg2(0, Screen_rows));
334
//#define ATEOJ_REPORT
339
"\n\t device = %s, ncurses = v%s"
340
"\n\t max_colors = %d, max_pairs = %d"
341
"\n\t Cap_can_goto = %s"
342
"\n\t Screen_cols = %d, Screen_rows = %d"
343
"\n\t Max_lines = %d, most recent Pseudo_size = %d"
350
, ttyname(STDOUT_FILENO), NCURSES_VERSION
351
, max_colors, max_pairs
352
, Cap_can_goto ? "yes" : "No!"
353
, Screen_cols, Screen_rows
354
, Max_lines, Pseudo_size
359
"\n\t Stdout_buf = %d, BUFSIZ = %u"
361
"\n\tWindows and Curwin->"
362
"\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d"
363
"\n\t rc.winname = %s, grpname = %s"
364
"\n\t rc.winflags = %08x, maxpflgs = %d"
365
"\n\t rc.fieldscur = %s"
366
"\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d"
367
"\n\t rc.sortindx = %d"
370
, sizeof(Stdout_buf), (unsigned)BUFSIZ
372
, sizeof(WIN_t), GROUPSMAX
373
, Curwin->rc.winname, Curwin->grpname
374
, Curwin->rc.winflags, Curwin->maxpflgs
375
, Curwin->rc.fieldscur
376
, Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln
377
, Curwin->rc.sortindx
382
"\n\t Linux version = %u.%u.%u, %s"
383
"\n\t Hertz = %u (%u bytes, %u-bit time)"
384
"\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u"
385
"\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)"
387
, LINUX_VERSION_MAJOR(linux_version_code)
388
, LINUX_VERSION_MINOR(linux_version_code)
389
, LINUX_VERSION_PATCH(linux_version_code)
391
, (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8
392
, Page_size, Cpu_tot, sizeof(proc_t)
393
, sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t)
399
if (str) fputs(str, fp);
405
* Normal end of execution.
407
* SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
408
// FIXME: can't do this shit in a signal handler
409
static void end_pgm (int sig) NORETURN;
410
static void end_pgm (int sig)
413
sig |= 0x80; // for a proper process exit code
414
bye_bye(stdout, sig, NULL);
419
* Standard error handler to normalize the look of all err o/p */
420
static void std_err (const char *str) NORETURN;
421
static void std_err (const char *str)
423
static char buf[SMLBUFSIZ];
426
/* we'll use our own buffer so callers can still use fmtmk() and, yes the
427
leading tab is not the standard convention, but the standard is wrong
428
-- OUR msg won't get lost in screen clutter, like so many others! */
429
snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
431
fprintf(stderr, "%s\n", buf);
434
/* not to worry, he'll change our exit code to 1 due to 'buf' */
435
bye_bye(stderr, 1, buf);
440
* Standard out handler */
441
static void std_out (const char *str) NORETURN;
442
static void std_out (const char *str)
444
static char buf[SMLBUFSIZ];
447
/* we'll use our own buffer so callers can still use fmtmk() and, yes the
448
leading tab is not the standard convention, but the standard is wrong
449
-- OUR msg won't get lost in screen clutter, like so many others! */
450
snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
452
fprintf(stdout, "%s\n", buf);
455
bye_bye(stdout, 0, buf);
462
* SIGTSTP, SIGTTIN and SIGTTOU */
463
// FIXME: can't do this shit in a signal handler!
464
static void suspend (int dont_care_sig)
468
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
469
putp(tg2(0, Screen_rows));
475
/* later, after SIGCONT... */
478
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
484
/*###### Misc Color/Display support ####################################*/
486
/* macro to test if a basic (non-color) capability is valid
487
thanks: Floyd Davidson <floyd@ptialaska.net> */
488
#define tIF(s) s ? s : ""
489
#define CAPCOPY(dst,src) src && strcpy(dst,src)
492
* Make the appropriate caps/color strings and set some
493
* lengths which are used to distinguish twix the displayed
494
* columns and an actual printed row!
495
* note: we avoid the use of background color so as to maximize
496
* compatibility with the user's xterm settings */
497
static void capsmk (WIN_t *q)
499
static int capsdone = 0;
501
// we must NOT disturb our 'empty' terminfo strings!
504
// these are the unchangeable puppies, so we only do 'em once
506
CAPCOPY(Cap_clr_eol, clr_eol);
507
CAPCOPY(Cap_clr_eos, clr_eos);
508
CAPCOPY(Cap_clr_scr, clear_screen);
510
if (!eat_newline_glitch) { // we like the eat_newline_glitch
511
CAPCOPY(Cap_rmam, exit_am_mode);
512
CAPCOPY(Cap_smam, enter_am_mode);
513
if (!*Cap_rmam || !*Cap_smam) { // need both
516
if (auto_right_margin) {
517
avoid_last_column = 1;
522
CAPCOPY(Cap_curs_huge, cursor_visible);
523
CAPCOPY(Cap_curs_norm, cursor_normal);
524
CAPCOPY(Cap_home, cursor_home);
525
CAPCOPY(Cap_norm, exit_attribute_mode);
526
CAPCOPY(Cap_reverse, enter_reverse_mode);
528
snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
529
if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
532
/* the key to NO run-time costs for configurable colors -- we spend a
533
little time with the user now setting up our terminfo strings, and
534
the job's done until he/she/it has a change-of-heart */
535
strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode));
536
if (CHKw(q, Show_COLORS) && max_colors > 0) {
537
strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr));
538
snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
539
, tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
540
snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
541
, tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
542
snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
543
, tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
544
snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
545
, Caps_off, tparm(set_a_foreground, q->rc.taskclr));
547
q->capclr_sum[0] = '\0';
548
strcpy(q->capclr_msg, Cap_reverse);
549
strcpy(q->capclr_pmt, q->cap_bold);
550
strcpy(q->capclr_hdr, Cap_reverse);
551
strcpy(q->capclr_rownorm, Cap_norm);
553
// composite(s), so we do 'em outside and after the if
554
snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
555
, q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
556
q->len_rownorm = strlen(q->capclr_rownorm);
557
q->len_rowhigh = strlen(q->capclr_rowhigh);
563
// Show an error, but not right now.
564
// Due to the postponed opening of ksym, using open_psdb_message,
565
// if P_WCH had been selected and the program is restarted, the
566
// message would otherwise be displayed prematurely.
567
static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2)));
568
static void msg_save (const char *fmts, ...)
574
vsnprintf(tmp, sizeof(tmp), fmts, va);
576
/* we'll add some extra attention grabbers to whatever this is */
577
snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp));
583
* Show an error message (caller may include a '\a' for sound) */
584
static void show_msg (const char *str)
600
* Show an input prompt + larger cursor */
601
static void show_pmt (const char *str)
615
* Show lines with specially formatted elements, but only output
616
* what will fit within the current screen width.
617
* Our special formatting consists of:
618
* "some text <_delimiter_> some more text <_delimiter_>...\n"
619
* Where <_delimiter_> is a single byte in the range of:
620
* \01 through \10 (in decimalizee, 1 - 8)
621
* and is used to select an 'attribute' from a capabilities table
622
* which is then applied to the *preceding* substring.
623
* Once recognized, the delimiter is replaced with a null character
624
* and viola, we've got a substring ready to output! Strings or
625
* substrings without delimiters will receive the Cap_norm attribute.
628
* This routine treats all non-delimiter bytes as displayable
629
* data subject to our screen width marching orders. If callers
630
* embed non-display data like tabs or terminfo strings in our
631
* glob, a line will truncate incorrectly at best. Worse case
632
* would be truncation of an embedded tty escape sequence.
634
* Tabs must always be avoided or our efforts are wasted and
635
* lines will wrap. To lessen but not eliminate the risk of
636
* terminfo string truncation, such non-display stuff should
637
* be placed at the beginning of a "short" line.
638
* (and as for tabs, gimme 1 more color then no worries, mate) */
639
static void show_special (int interact, const char *glob)
640
{ /* note: the following is for documentation only,
641
the real captab is now found in a group's WIN_t !
642
+------------------------------------------------------+
643
| char *captab[] = { : Cap's/Delim's |
644
| Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 |
646
| Msg_color, Pmt_color, = \04, \05 |
648
| Row_color_high, = \07 |
649
| Row_color_norm }; = \10 [octal!] |
650
+------------------------------------------------------+ */
651
char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ];
652
char *rp, *cap, *lin_end, *sub_beg, *sub_end;
655
/* handle multiple lines passed in a bunch */
656
while ((lin_end = strchr(glob, '\n'))) {
658
/* create a local copy we can extend and otherwise abuse */
659
size_t amt = lin_end - glob;
660
if(amt > sizeof lin - 1)
661
amt = sizeof lin - 1; // shit happens
662
memcpy(lin, glob, amt);
663
/* zero terminate this part and prepare to parse substrings */
666
sub_beg = sub_end = lin;
671
case 0: /* no end delim, captab makes normal */
672
*(sub_end + 1) = '\0'; /* extend str end, then fall through */
674
cap = Curwin->captab[(int)*sub_end];
676
snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
678
if(rp - row + amt + 1 > sizeof row)
679
goto overflow; // shit happens
681
room -= (sub_end - sub_beg);
684
default: /* nothin' special, just text */
687
if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
690
if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
691
else PUFF("%s%s\n", row, Cap_clr_eol);
692
glob = ++lin_end; /* point to next line (maybe) */
693
} /* end: while 'lines' */
695
/* If there's anything left in the glob (by virtue of no trailing '\n'),
696
it probably means caller wants to retain cursor position on this final
697
line. That, in turn, means we're interactive and so we'll just do our
698
'fit-to-screen' thingy... */
699
if (*glob) PUTT("%.*s", Screen_cols, glob);
703
/*###### Small Utility routines ########################################*/
705
// Get a string from the user
706
static char *ask4str (const char *prompt)
708
static char buf[GETBUFSIZ];
711
memset(buf, '\0', sizeof(buf));
712
chin(1, buf, sizeof(buf) - 1);
718
// Get a float from the user
719
static float get_float (const char *prompt)
724
if (!(*(line = ask4str(prompt)))) return -1;
725
// note: we're not allowing negative floats
726
if (strcspn(line, ",.1234567890")) {
727
show_msg("\aNot valid");
730
sscanf(line, "%f", &f);
735
// Get an integer from the user
736
static int get_int (const char *prompt)
741
if (!(*(line = ask4str(prompt)))) return -1;
742
// note: we've got to allow negative ints (renice)
743
if (strcspn(line, "-1234567890")) {
744
show_msg("\aNot valid");
747
sscanf(line, "%d", &n);
753
* Do some scaling stuff.
754
* We'll interpret 'num' as one of the following types and
755
* try to format it to fit 'width'.
756
* SK_no (0) it's a byte count
757
* SK_Kb (1) it's kilobytes
758
* SK_Mb (2) it's megabytes
759
* SK_Gb (3) it's gigabytes
760
* SK_Tb (4) it's terabytes */
761
static const char *scale_num (unsigned long num, const int width, const unsigned type)
763
/* kilobytes, megabytes, gigabytes, terabytes, duh! */
764
static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 };
765
/* kilo, mega, giga, tera, none */
767
static char nextup[] = { 'K', 'M', 'G', 'T', 0 };
769
static char nextup[] = { 'k', 'm', 'g', 't', 0 };
771
static char buf[TNYBUFSIZ];
775
/* try an unscaled version first... */
776
if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf;
778
/* now try successively higher types until it fits */
779
for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
780
/* the most accurate version */
781
if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up))
783
/* the integer version */
784
if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up))
787
/* well shoot, this outta' fit... */
793
* Do some scaling stuff.
794
* format 'tics' to fit 'width'. */
795
static const char *scale_tics (TIC_t tics, const int width)
806
static char buf[TNYBUFSIZ];
807
unsigned long nt; // narrow time, for speed on 32-bit
808
unsigned cc; // centiseconds
809
unsigned nn; // multi-purpose whatever
811
nt = (tics * 100ull) / Hertz;
812
cc = nt % 100; // centiseconds past second
813
nt /= 100; // total seconds
814
nn = nt % 60; // seconds past the minute
815
nt /= 60; // total minutes
816
if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
818
if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
820
nn = nt % 60; // minutes past the hour
821
nt /= 60; // total hours
822
if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
824
nn = nt; // now also hours
825
if (width >= snprintf(buf, sizeof buf, HH, nn))
827
nn /= 24; // now days
828
if (width >= snprintf(buf, sizeof buf, DD, nn))
830
nn /= 7; // now weeks
831
if (width >= snprintf(buf, sizeof buf, WW, nn))
833
// well shoot, this outta' fit...
843
static int selection_type;
844
static uid_t selection_uid;
846
// FIXME: this is "temporary" code we hope
847
static int good_uid(const proc_t *restrict const pp){
848
switch(selection_type){
854
if (pp->ruid == selection_uid) return 1;
855
if (pp->suid == selection_uid) return 1;
856
if (pp->fuid == selection_uid) return 1;
859
if (pp->euid == selection_uid) return 1;
862
; // don't know what it is; find bugs fast
867
// swiped from ps, and ought to be in libproc
868
static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){
869
struct passwd *passwd_data;
872
static const char uidrange[] = "User ID out of range.";
873
static const char uidexist[] = "User name does not exist.";
874
num = strtoul(str, &endp, 0);
875
if(*endp != '\0'){ /* hmmm, try as login name */
876
passwd_data = getpwnam(str);
877
if(!passwd_data) return uidexist;
878
num = passwd_data->pw_uid;
880
if(num > 0xfffffffeUL) return uidrange;
886
/*###### Library Alternatives ##########################################*/
889
* Handle our own memory stuff without the risk of leaving the
890
* user's terminal in an ugly state should things go sour. */
892
static void *alloc_c (unsigned numb) MALLOC;
893
static void *alloc_c (unsigned numb)
898
if (!(p = calloc(1, numb)))
899
std_err("failed memory allocate");
903
static void *alloc_r (void *q, unsigned numb) MALLOC;
904
static void *alloc_r (void *q, unsigned numb)
909
if (!(p = realloc(q, numb)))
910
std_err("failed memory allocate");
916
* This guy's modeled on libproc's 'five_cpu_numbers' function except
917
* we preserve all cpu data in our CPU_t array which is organized
919
* cpus[0] thru cpus[n] == tics for each separate cpu
920
* cpus[Cpu_tot] == tics from the 1st /proc/stat line */
921
static CPU_t *cpus_refresh (CPU_t *cpus)
923
static FILE *fp = NULL;
926
// enough for a /proc/stat CPU line (not the intr line)
929
/* by opening this file once, we'll avoid the hit on minor page faults
930
(sorry Linux, but you'll have to close it for us) */
932
if (!(fp = fopen("/proc/stat", "r")))
933
std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
934
/* note: we allocate one more CPU_t than Cpu_tot so that the last slot
935
can hold tics representing the /proc/stat cpu summary (the first
936
line read) -- that slot supports our View_CPUSUM toggle */
937
cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
942
// first value the last slot with the cpu summary line
943
if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
944
cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
945
cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
946
cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number
947
num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
958
std_err("failed /proc/stat read");
960
// and just in case we're 2.2.xx compiled without SMP support...
963
memcpy(cpus, &cpus[1], sizeof(CPU_t));
966
// now value each separate cpu's tics
967
for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
968
if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
969
cpus[i].x = 0; // FIXME: can't tell by kernel version number
970
cpus[i].y = 0; // FIXME: can't tell by kernel version number
971
cpus[i].z = 0; // FIXME: can't tell by kernel version number
972
num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
974
&cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z
977
std_err("failed /proc/stat read");
984
* Refresh procs *Helper* function to eliminate yet one more need
985
* to loop through our darn proc_t table. He's responsible for:
986
* 1) calculating the elapsed time since the previous frame
987
* 2) counting the number of tasks in each state (run, sleep, etc)
988
* 3) maintaining the HST_t's and priming the proc_t pcpu field
989
* 4) establishing the total number tasks for this frame */
990
static void prochlp (proc_t *this)
992
static HST_t *hist_sav = NULL;
993
static HST_t *hist_new = NULL;
994
static unsigned hist_siz = 0; // number of structs
995
static unsigned maxt_sav; // prior frame's max tasks
998
if (unlikely(!this)) {
999
static struct timeval oldtimev;
1000
struct timeval timev;
1001
struct timezone timez;
1005
gettimeofday(&timev, &timez);
1006
et = (timev.tv_sec - oldtimev.tv_sec)
1007
+ (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
1008
oldtimev.tv_sec = timev.tv_sec;
1009
oldtimev.tv_usec = timev.tv_usec;
1011
// if in Solaris mode, adjust our scaling for all cpus
1012
Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
1013
maxt_sav = Frame_maxtask;
1014
Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
1016
// reuse memory each time around
1017
hist_tmp = hist_sav;
1018
hist_sav = hist_new;
1019
hist_new = hist_tmp;
1020
// prep for our binary search by sorting the last frame's HST_t's
1021
qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
1025
switch (this->state) {
1041
if (unlikely(Frame_maxtask+1 >= hist_siz)) {
1042
hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25%
1043
hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz);
1044
hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz);
1046
/* calculate time in this process; the sum of user time (utime) and
1047
system time (stime) -- but PLEASE dont waste time and effort on
1048
calcs and saves that go unused, like the old top! */
1049
hist_new[Frame_maxtask].pid = this->tid;
1050
hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
1055
int hi = maxt_sav - 1;
1057
// find matching entry from previous frame and make ticks elapsed
1060
if (this->tid < hist_sav[i].pid)
1062
else if (likely(this->tid > hist_sav[i].pid))
1065
tics -= hist_sav[i].tics;
1074
tmp.pid = this->tid;
1075
ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
1076
if(ptr) tics -= ptr->tics;
1080
// we're just saving elapsed tics, to be converted into %cpu if
1081
// this task wins it's displayable screen row lottery... */
1083
// if (Frames_maxcmdln) { }
1084
// shout this to the world with the final call (or us the next time in)
1090
* This guy's modeled on libproc's 'readproctab' function except
1091
* we reuse and extend any prior proc_t's. He's been customized
1092
* for our specific needs and to avoid the use of <stdarg.h> */
1093
static proc_t **procs_refresh (proc_t **table, int flags)
1095
#define PTRsz sizeof(proc_t *)
1096
#define ENTsz sizeof(proc_t)
1097
static unsigned savmax = 0; // first time, Bypass: (i)
1098
proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii)
1099
unsigned curmax = 0; // every time (jeeze)
1101
static int show_threads_was_enabled = 0; // optimization
1103
prochlp(NULL); // prep for a new frame
1105
PT = openproc(flags, Monpids);
1107
PT = openproc(flags);
1110
std_err(strerror(errno));
1115
// i) Allocated Chunks: *Existing* table; refresh + reuse
1116
if (!(CHKw(Curwin, Show_THREADS))) {
1117
while (curmax < savmax) {
1118
if (table[curmax]->cmdline) {
1120
// Skip if Show_THREADS was never enabled
1121
if (show_threads_was_enabled) {
1122
for (idx = curmax + 1; idx < savmax; idx++) {
1123
if (table[idx]->cmdline == table[curmax]->cmdline)
1124
table[idx]->cmdline = NULL;
1127
free(*table[curmax]->cmdline);
1128
table[curmax]->cmdline = NULL;
1130
if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
1131
prochlp(ptsk); // tally & complete this proc_t
1135
else { // show each thread in a process separately
1136
while (curmax < savmax) {
1138
if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
1139
show_threads_was_enabled = 1;
1140
while (curmax < savmax) {
1142
if (table[curmax]->cmdline) {
1143
// threads share the same cmdline storage. 'table' is
1144
// qsort()ed, so must look through the rest of the table.
1145
for (idx = curmax + 1; idx < savmax; idx++) {
1146
if (table[idx]->cmdline == table[curmax]->cmdline)
1147
table[idx]->cmdline = NULL;
1149
free(*table[curmax]->cmdline); // only free once
1150
table[curmax]->cmdline = NULL;
1152
if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
1156
free(ptsk); // readproc() proc_t not used
1160
// ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
1161
if (!(CHKw(Curwin, Show_THREADS))) {
1163
// realloc as we go, keeping 'table' ahead of 'currmax++'
1164
table = alloc_r(table, (curmax + 1) * PTRsz);
1165
// here, readproc will allocate the underlying proc_t stg
1166
if (likely(ptsk = readproc(PT, NULL))) {
1167
prochlp(ptsk); // tally & complete this proc_t
1168
table[curmax++] = ptsk;
1172
else { // show each thread in a process separately
1175
if (likely(ptsk = readproc(PT, NULL))) {
1176
show_threads_was_enabled = 1;
1178
table = alloc_r(table, (curmax + 1) * PTRsz);
1179
if (!(ttsk = readtask(PT, ptsk, NULL))) break;
1181
table[curmax++] = ttsk;
1183
free(ptsk); // readproc() proc_t not used
1189
// iii) Chunkless: make 'eot' entry, after ensuring proc_t exists
1190
if (curmax >= savmax) {
1191
table = alloc_r(table, (curmax + 1) * PTRsz);
1192
// here, we must allocate the underlying proc_t stg ourselves
1193
table[curmax] = alloc_c(ENTsz);
1194
savmax = curmax + 1;
1196
// this frame's end, but not necessarily end of allocated space
1197
table[curmax]->tid = -1;
1204
/*###### Field Table/RCfile compatability support ######################*/
1206
// from either 'stat' or 'status' (preferred), via bits not otherwise used
1207
#define L_EITHER PROC_SPARE_1
1208
// These are the Fieldstab.lflg values used here and in reframewins.
1209
// (own identifiers as documentation and protection against changes)
1210
#define L_stat PROC_FILLSTAT
1211
#define L_statm PROC_FILLMEM
1212
#define L_status PROC_FILLSTATUS
1213
#define L_CMDLINE L_EITHER | PROC_FILLARG
1214
#define L_EUSER PROC_FILLUSR
1215
#define L_RUSER L_status | PROC_FILLUSR
1216
#define L_GROUP L_status | PROC_FILLGRP
1218
// for reframewins and summary_show 1st pass
1219
#define L_DEFAULT PROC_FILLSTAT
1221
// a temporary macro, soon to be undef'd...
1222
#define SF(f) (QFP_t)sort_P_ ## f
1224
/* These are our gosh darn 'Fields' !
1225
They MUST be kept in sync with pflags !!
1226
note: for integer data, the length modifiers found in .fmts may
1227
NOT reflect the true field type found in proc_t -- this plus
1228
a cast when/if displayed provides minimal width protection. */
1229
static FLD_t Fieldstab[] = {
1231
P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid)
1232
P_CPU, L_stat - never filled by libproc, but requires times (pcpu)
1233
P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline)
1234
L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit !
1235
keys head fmts width scale sort desc lflg
1236
------ ----------- ------- ------ ----- ----- ---------------------- -------- */
1237
{ "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE },
1238
{ "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER },
1239
{ "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER },
1240
{ "DdCc", " UID", " %5u", -1, -1, SF(UID), "User Id", L_NONE },
1241
{ "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER },
1242
{ "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP },
1243
{ "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat },
1244
{ "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat },
1245
{ "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat },
1246
{ "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
1247
{ "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat },
1248
{ "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat },
1249
{ "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat },
1250
{ "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm },
1251
{ "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm },
1252
{ "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm },
1253
{ "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm },
1254
{ "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm },
1255
{ "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm },
1256
{ "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm },
1257
{ "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat },
1258
{ "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm },
1259
{ "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER },
1260
// next entry's special: '.head' will be formatted using table entry's own
1261
// '.fmts' plus runtime supplied conversion args!
1262
{ "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER },
1263
{ "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat },
1264
// next entry's special: the 0's will be replaced with '.'!
1265
{ "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags <sched.h>", L_stat },
1267
{ "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat },
1268
{ "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat },
1269
{ "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat },
1270
{ "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
1271
{ "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 },
1272
{ "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
1273
{ "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
1279
/* All right, those-that-follow -- Listen Up!
1280
* For the above table keys and the following present/future rc file
1281
* compatibility support, you have Mr. Albert D. Cahalan to thank.
1282
* He must have been in a 'Christmas spirit'. Were it left to me,
1283
* this top would never have gotten that close to the former top's
1284
* crufty rcfile. Not only is it illogical, it's odoriferous !
1287
// used as 'to' and/or 'from' args in the ft_xxx utilities...
1288
#define FT_NEW_fmt 0
1289
#define FT_OLD_fmt 2
1293
// convert, or 0 for failure
1294
static int ft_cvt_char (const int fr, const int to, int c) {
1297
while (++j < MAXTBL(Fieldstab)) {
1298
if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to];
1299
if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1];
1307
static inline int ft_get_char (const int fr, int i) {
1309
if (i < 0) return 0;
1310
if (i >= MAXTBL(Fieldstab)) return 0;
1311
c = Fieldstab[i].keys[fr];
1312
if (c == '.') c = 0; // '.' marks a bad entry
1318
// convert, or -1 for failure
1319
static int ft_get_idx (const int fr, int c) {
1322
while (++j < MAXTBL(Fieldstab)) {
1323
if (c == Fieldstab[j].keys[fr]) return j;
1324
if (c == Fieldstab[j].keys[fr+1]) return j;
1331
// convert, or NULL for failure
1332
static const FLD_t *ft_get_ptr (const int fr, int c) {
1335
while (++j < MAXTBL(Fieldstab)) {
1336
if (c == Fieldstab[j].keys[fr]) return Fieldstab+j;
1337
if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j;
1344
// convert, or NULL for failure
1345
static const FLD_t *ft_idx_to_ptr (const int i) {
1346
if (i < 0) return NULL;
1347
if (i >= MAXTBL(Fieldstab)) return NULL;
1348
return Fieldstab + i;
1352
// convert, or -1 for failure
1353
static int ft_ptr_to_idx (const FLD_t *p) {
1355
if (p < Fieldstab) return -1;
1357
if (i >= MAXTBL(Fieldstab)) return -1;
1364
static void rc_bugless (const RCF_t *const rc) {
1368
fprintf(stderr,"\n%d %d %f %d\n",
1369
rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
1373
fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n",
1374
w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks,
1375
w->summclr, w->msgsclr, w->headclr, w->taskclr
1382
// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window.
1383
// line 1: an eyecatcher, with a shameless advertisement
1384
// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin.
1385
// For each of the 4 windows:
1386
// line a: contains winname, fieldscur
1387
// line b: contains winflags, sortindx, maxtasks
1388
// line c: contains summclr, msgsclr, headclr, taskclr
1389
// line d: if present, would crash procps-3.1.1
1390
static int rc_read_new (const char *const buf, RCF_t *rc) {
1395
cp = strstr(buf, "\n\n" RCF_EYECATCHER);
1397
cp = strchr(cp + 2, '\n');
1398
if (!cp++) return -2;
1400
cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n",
1401
&rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index
1403
if (cnt != 4) return -3;
1404
cp = strchr(cp, '\n');
1405
if (!cp++) return -4;
1407
for (i = 0; i < GROUPSMAX; i++) {
1408
RCW_t *ptr = &rc->win[i];
1409
cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur);
1410
if (cnt != 2) return 5+100*i; // OK to have less than 4 windows
1411
if (WINNAMSIZ <= strlen(ptr->winname)) return -6;
1412
if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7;
1413
cp = strchr(cp, '\n');
1414
if (!cp++) return -(8+100*i);
1416
cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
1417
&ptr->winflags, &ptr->sortindx, &ptr->maxtasks
1419
if (cnt != 3) return -(9+100*i);
1420
cp = strchr(cp, '\n');
1421
if (!cp++) return -(10+100*i);
1423
cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
1424
&ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
1426
if (cnt != 4) return -(11+100*i);
1427
cp = strchr(cp, '\n');
1428
if (!cp++) return -(12+100*i);
1429
while (*cp == '\t') { // skip unknown per-window settings
1430
cp = strchr(cp, '\n');
1431
if (!cp++) return -(13+100*i);
1439
static int rc_read_old (const char *const buf, RCF_t *rc) {
1440
const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......";
1441
const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr";
1444
unsigned c_show = 0;
1445
int badchar = 0; // allow a limited number of duplicates and junk
1447
char scoreboard[256];
1448
memset(scoreboard, '\0', sizeof scoreboard);
1450
cp = buf+2; // skip the "\n\n" we stuck at the beginning
1455
if (u+1 >= sizeof rc->win[0].fieldscur) return -1;
1456
if (c == '\0') return -2;
1457
if (c == '\n') break;
1458
if (c & ~0x7f) return -3;
1459
if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden
1460
if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed
1461
scoreboard[c|0xe0u]++;
1462
tmp = strchr(old,c);
1464
c = *((tmp-old)+std);
1465
if (c == '.') continue;
1466
if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed
1467
scoreboard[c&0x1fu]++;
1468
rc->win[0].fieldscur[u++] = c;
1470
rc->win[0].fieldscur[u++] = '\0';
1471
if (u < 21) return -6; // catch junk, not good files (had 23 chars in one)
1472
if (u > 33) return -7; // catch junk, not good files (had 29 chars in one)
1473
// fprintf(stderr, "badchar: %d\n", badchar); sleep(2);
1474
if (badchar > 8) return -8; // too much junk
1475
if (!c_show) return -9; // nothing was shown
1477
// rest of file is optional, but better look right if it exists
1478
if (!*cp) return 12;
1479
if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid?
1480
rc->delay_time = *cp - '0';
1482
memset(scoreboard, '\0', sizeof(scoreboard));
1484
int c = *++cp & 0xffu; // protect scoreboard[] from negative char
1485
if (!c) return -14; // not OK to hit EOL w/o '\n'
1486
if (c == '\n') break;
1491
return -15; // not supposed to have digits here
1493
// case 's': // mostly for global rcfile
1494
// rc->mode_secure = 1;
1497
rc->win[0].winflags |= Show_CTIMES;
1500
rc->win[0].winflags |= Show_CMDLIN;
1503
rc->win[0].winflags &= ~Show_IDLEPS;
1506
rc->win[0].winflags |= Show_THREADS;
1509
rc->win[0].winflags &= ~View_MEMORY;
1512
rc->win[0].winflags &= ~View_LOADAV;
1515
rc->win[0].winflags &= ~View_STATES;
1518
rc->mode_irixps = 0;
1522
c = 0; // for scoreboard
1523
rc->win[0].sortindx = P_MEM;
1526
c = 0; // for scoreboard
1527
rc->win[0].sortindx = P_CPU;
1529
case 'A': // supposed to be start_time
1530
c = 0; // for scoreboard
1531
rc->win[0].sortindx = P_PID;
1534
c = 0; // for scoreboard
1535
rc->win[0].sortindx = P_TM2;
1538
c = 0; // for scoreboard
1539
rc->win[0].sortindx = P_PID;
1543
// just ignore it, except for the scoreboard of course
1546
if (scoreboard[c]) return -16; // duplicates not allowed
1553
static void rc_write_new (FILE *fp) {
1556
fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
1559
fprintf(fp, RCF_DEPRECATED
1560
"Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n",
1561
Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk)
1563
for (i = 0; i < GROUPSMAX; i++) {
1565
char *cp = Winstk[i].rc.fieldscur;
1569
int c = *cp++ & 0xff;
1574
continue; // throw away junk (some of it)
1576
buf[j++] = c; // gets the '\0' too
1580
fprintf(fp, "%s\tfieldscur=%s\n",
1581
Winstk[i].rc.winname, buf
1583
fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
1584
Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
1586
fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n",
1587
Winstk[i].rc.summclr, Winstk[i].rc.msgsclr,
1588
Winstk[i].rc.headclr, Winstk[i].rc.taskclr
1594
static const char *rc_write_whatever (void) {
1595
FILE *fp = fopen(Rc_name, "w");
1597
if (!fp) return strerror(errno);
1604
/*###### Startup routines ##############################################*/
1606
// No mater what *they* say, we handle the really really BIG and
1607
// IMPORTANT stuff upon which all those lessor functions depend!
1608
static void before (char *me)
1612
/* setup our program name -- big! */
1613
Myname = strrchr(me, '/');
1614
if (Myname) ++Myname; else Myname = me;
1616
/* establish cpu particulars -- even bigger! */
1617
Cpu_tot = smp_num_cpus;
1618
if (linux_version_code > LINUX_VERSION(2, 5, 41))
1619
States_fmts = STATES_line2x5;
1620
if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-(
1621
States_fmts = STATES_line2x6;
1622
if (linux_version_code >= LINUX_VERSION(2, 6, 11))
1623
States_fmts = STATES_line2x7;
1625
/* get virtual page size -- nearing huge! */
1626
Page_size = getpagesize();
1633
pcpu_max_value = 99.9;
1635
Fieldstab[P_CPN].head = " P";
1636
Fieldstab[P_CPN].fmts = " %1u";
1638
Fieldstab[P_CPN].head = " P";
1639
Fieldstab[P_CPN].fmts = " %2u";
1641
if(smp_num_cpus>99){
1642
Fieldstab[P_CPN].head = " P";
1643
Fieldstab[P_CPN].fmts = " %3u";
1645
if(smp_num_cpus>999){
1646
Fieldstab[P_CPN].head = " P";
1647
Fieldstab[P_CPN].fmts = " %4u";
1651
static char pid_fmt[6];
1652
unsigned pid_digits = get_pid_digits();
1653
if(pid_digits<4) pid_digits=4;
1654
snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits);
1655
Fieldstab[P_PID].fmts = pid_fmt;
1656
Fieldstab[P_PID].head = " PID" + 10 - pid_digits;
1657
Fieldstab[P_PPD].fmts = pid_fmt;
1658
Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits;
1663
// Config file read *helper* function.
1664
// Anything missing won't show as a choice in the field editor,
1665
// so make sure there is exactly one of each letter.
1667
// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x
1668
// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too.
1669
static void confighlp (char *fields) {
1670
unsigned upper[PFLAGSSIZ];
1671
unsigned lower[PFLAGSSIZ];
1675
memset(upper, '\0', sizeof upper);
1676
memset(lower, '\0', sizeof lower);
1682
if(isupper(c)) upper[c&0x1f]++;
1683
else lower[c&0x1f]++;
1688
if (upper[c&0x1f] && lower[c&0x1f]) {
1689
lower[c&0x1f] = 0; // got both, so wipe out unseen column
1691
cp = strchr(fields, c);
1692
if (cp) memmove(cp, cp+1, strlen(cp));
1696
while (lower[c&0x1f] > 1) { // got too many a..z
1698
cp = strchr(fields, c);
1699
memmove(cp, cp+1, strlen(cp));
1701
while (upper[c&0x1f] > 1) { // got too many A..Z
1703
cp = strchr(fields, toupper(c));
1704
memmove(cp, cp+1, strlen(cp));
1706
if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing
1708
memmove(fields+1, fields, strlen(fields)+1);
1716
// First attempt to read the /etc/rcfile which contains two lines
1717
// consisting of the secure mode switch and an update interval.
1718
// It's presence limits what ordinary users are allowed to do.
1719
// (it's actually an old-style config file)
1721
// Then build the local rcfile name and try to read a crufty old-top
1722
// rcfile (whew, odoriferous), which may contain an embedded new-style
1723
// rcfile. Whether embedded or standalone, new-style rcfile values
1724
// will always override that crufty stuff!
1725
// note: If running in secure mode via the /etc/rcfile,
1726
// Delay_time will be ignored except for root.
1727
static void configs_read (void)
1729
const RCF_t def_rcf = DEF_RCFILE;
1730
char fbuf[MEDBUFSIZ];
1733
float delay = Rc.delay_time;
1735
// read part of an old-style config in /etc/toprc
1736
fd = open(SYS_RCFILESPEC, O_RDONLY);
1739
num = read(fd, fbuf, sizeof(fbuf) - 1);
1741
const char *sec = strchr(fbuf, 's');
1742
const char *eol = strchr(fbuf, '\n');
1744
const char *two = eol + 1; // line two
1745
if (sec < eol) Secure_mode = !!sec;
1746
eol = strchr(two, '\n');
1747
if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two);
1753
if (getenv("TOPRC")) { // should switch on Myname before documenting this?
1754
// not the most optimal here...
1755
snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC"));
1757
snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew...
1759
snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
1763
fd = open(Rc_name, O_RDONLY);
1766
num = read(fd, fbuf+2, sizeof(fbuf) -3);
1771
//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
1773
if (rc_read_new(fbuf, &rcf) < 0) {
1774
rcf = def_rcf; // on failure, maybe mangled
1775
if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf;
1777
delay = rcf.delay_time;
1782
// update Rc defaults, establish a Curwin and fix up the window stack
1783
Rc.mode_altscr = rcf.mode_altscr;
1784
Rc.mode_irixps = rcf.mode_irixps;
1785
if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0;
1786
Curwin = &Winstk[rcf.win_index];
1787
for (i = 0; i < GROUPSMAX; i++) {
1788
memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]);
1789
confighlp(Winstk[i].rc.fieldscur);
1792
if(Rc.mode_irixps && smp_num_cpus>1 &&
1793
!(CHKw(Curwin, Show_THREADS))) {
1794
// good for 100 CPUs per process
1795
pcpu_max_value = 9999.0;
1796
Fieldstab[P_CPU].fmts = " %4.0f";
1799
// lastly, establish the true runtime secure mode and delay time
1800
if (!getuid()) Secure_mode = 0;
1801
if (!Secure_mode) Rc.delay_time = delay;
1805
// Parse command line arguments.
1806
// Note: it's assumed that the rc file(s) have already been read
1807
// and our job is to see if any of those options are to be
1808
// overridden -- we'll force some on and negate others in our
1809
// best effort to honor the loser's (oops, user's) wishes...
1810
static void parse_args (char **args)
1812
/* differences between us and the former top:
1813
-C (separate CPU states for SMP) is left to an rcfile
1814
-p (pid monitoring) allows a comma delimited list
1815
-q (zero delay) eliminated as redundant, incomplete and inappropriate
1816
use: "nice -n-10 top -d0" to achieve what was only claimed
1817
-c,i,S act as toggles (not 'on' switches) for enhanced user flexibility
1818
. no deprecated/illegal use of 'breakargv:' with goto
1819
. bunched args are actually handled properly and none are ignored
1820
. we tolerate NO whitespace and NO switches -- maybe too tolerant? */
1821
static const char usage[] =
1822
" -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]";
1823
float tmp_delay = MAXFLOAT;
1827
const char *cp = *(args++);
1838
TOGw(Curwin, Show_CMDLIN);
1842
else if (*args) cp = *args++;
1843
else std_err("-d requires argument");
1844
/* a negative delay will be dealt with shortly... */
1845
if (sscanf(cp, "%f", &tmp_delay) != 1)
1846
std_err(fmtmk("bad delay '%s'", cp));
1849
TOGw(Curwin, Show_THREADS);
1853
std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
1855
TOGw(Curwin, Show_IDLEPS);
1856
Curwin->rc.maxtasks = 0;
1860
else if (*args) cp = *args++;
1861
else std_err("-n requires argument");
1862
if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1)
1863
std_err(fmtmk("bad iterations arg '%s'", cp));
1867
if (selection_type && selection_type != 'p') std_err("conflicting process selection");
1868
selection_type = 'p';
1870
else if (*args) cp = *args++;
1871
else std_err("-p argument missing");
1872
if (Monpidsidx >= MONPIDMAX)
1873
std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
1874
if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0)
1875
std_err(fmtmk("bad pid '%s'", cp));
1876
if (!Monpids[Monpidsidx])
1877
Monpids[Monpidsidx] = getpid();
1879
if (!(p = strchr(cp, ',')))
1888
TOGw(Curwin, Show_CTIMES);
1893
if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
1895
else if (*args) cp = *args++;
1896
else std_err("-u missing name");
1897
errmsg = parse_uid(cp, &selection_uid);
1898
if (errmsg) std_err(errmsg);
1899
selection_type = 'u';
1900
cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
1906
if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
1908
else if (*args) cp = *args++;
1909
else std_err("-u missing name");
1910
errmsg = parse_uid(cp, &selection_uid);
1911
if (errmsg) std_err(errmsg);
1912
selection_type = 'U';
1913
cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
1917
std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
1918
, *cp, Myname, usage));
1920
} /* end: switch (*cp) */
1922
/* advance cp and jump over any numerical args used above */
1923
if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1;
1924
} /* end: while (*cp) */
1925
} /* end: while (*args) */
1927
/* fixup delay time, maybe... */
1928
if (MAXFLOAT != tmp_delay) {
1929
if (Secure_mode || tmp_delay < 0)
1930
msg_save("Delay time Not changed");
1932
Rc.delay_time = tmp_delay;
1938
* Set up the terminal attributes */
1939
static void whack_terminal (void)
1941
struct termios newtty;
1944
setupterm("dumb", STDOUT_FILENO, NULL);
1947
setupterm(NULL, STDOUT_FILENO, NULL);
1948
if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
1949
std_err("failed tty get");
1951
newtty.c_lflag &= ~(ICANON | ECHO);
1952
newtty.c_oflag &= ~(TAB3);
1953
newtty.c_cc[VMIN] = 1;
1954
newtty.c_cc[VTIME] = 0;
1957
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
1959
std_err(fmtmk("failed tty set: %s", strerror(errno)));
1961
tcgetattr(STDIN_FILENO, &Rawtty);
1962
#ifndef STDOUT_IOLBF
1963
// thanks anyway stdio, but we'll manage buffering at the frame level...
1964
setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf));
1971
/*###### Field Selection/Ordering routines #############################*/
1974
// Display each field represented in the Fields Table along with its
1975
// description and mark (with a leading asterisk) fields associated
1976
// with upper case letter(s) in the passed 'fields string'.
1978
// After all fields have been displayed, some extra explanatory
1979
// text may also be output
1980
static void display_fields (const char *fields, const char *xtra)
1984
int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
1986
/* we're relying on callers to first clear the screen and thus avoid screen
1987
flicker if they're too lazy to handle their own asterisk (*) logic */
1988
putp(Curwin->cap_bold);
1989
for (i = 0; fields[i]; ++i) {
1990
const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]);
1991
int b = isupper(fields[i]);
1993
if (!f) continue; // hey, should be std_err!
1994
for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
1996
PUTT("%s%s%c %c: %-10s = %s",
1997
tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
1998
b ? Curwin->cap_bold : Cap_norm,
2006
putp(Curwin->capclr_rownorm);
2007
while ((p = strchr(xtra, '\n'))) {
2010
tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
2023
// Change order of displayed fields.
2024
static void fields_reorder (void)
2026
static const char prompt[] =
2027
"Upper case letter moves field left, lower case right";
2032
putp(Cap_curs_huge);
2034
display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2035
show_special(1, fmtmk(FIELDS_current
2036
, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2038
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2039
i = toupper(c) - 'A';
2040
if (((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2041
|| ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) {
2042
if (isupper(c)) p--;
2043
if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) {
2050
putp(Cap_curs_norm);
2053
// Select sort field.
2054
static void fields_sort (void)
2056
static const char prompt[] =
2057
"Select sort field via field letter, type any other key to return";
2058
char phoney[PFLAGSSIZ];
2062
strcpy(phoney, NUL_FIELDS);
2063
x = i = Curwin->rc.sortindx;
2065
putp(Cap_curs_huge);
2069
display_fields(phoney, SORT_xtra);
2070
show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
2072
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2073
i = toupper(c) - 'A';
2077
if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
2079
Curwin->rc.sortindx = x;
2080
putp(Cap_curs_norm);
2084
// Toggle displayed fields.
2085
static void fields_toggle (void)
2087
static const char prompt[] =
2088
"Toggle fields via field letter, type any other key to return";
2093
putp(Cap_curs_huge);
2095
display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2096
show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2098
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2099
i = toupper(c) - 'A';
2100
if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2102
else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
2105
putp(Cap_curs_norm);
2108
/*###### Windows/Field Groups support #################################*/
2110
// For each of the four windows:
2111
// 1) Set the number of fields/columns to display
2112
// 2) Create the field columns heading
2113
// 3) Set maximum cmdline length, if command lines are in use
2114
// In the process, the required PROC_FILLxxx flags will be rebuilt!
2115
static void reframewins (void)
2120
int i, needpsdb = 0;
2122
// Frames_libflags = 0; // should be called only when it's zero
2123
// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible
2126
if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) {
2127
// build window's procflags array and establish a tentative maxpflgs
2128
for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) {
2129
if (isupper(w->rc.fieldscur[i]))
2130
w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A';
2133
/* build a preliminary columns header not to exceed screen width
2134
while accounting for a possible leading window number */
2135
*(s = w->columnhdr) = '\0';
2136
if (Rc.mode_altscr) s = scat(s, " ");
2137
for (i = 0; i < w->maxpflgs; i++) {
2138
h = Fieldstab[w->procflags[i]].head;
2139
// oops, won't fit -- we're outta here...
2140
if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break;
2144
// establish the final maxpflgs and prepare to grow the command column
2145
// heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
2146
// but that's ok because it won't be displayed anyway
2148
w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
2150
// finally, we can build the true run-time columns header, format the
2151
// command column heading, if P_CMD is really being displayed, and
2152
// rebuild the all-important PROC_FILLxxx flags that will be used
2153
// until/if we're we're called again
2154
*(s = w->columnhdr) = '\0';
2155
// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
2156
for (i = 0; i < w->maxpflgs; i++) {
2157
int advance = (i==0) && !Rc.mode_altscr;
2158
h = Fieldstab[w->procflags[i]].head;
2159
if (P_WCH == w->procflags[i]) needpsdb = 1;
2160
if (P_CMD == w->procflags[i]) {
2161
s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ ));
2162
if (CHKw(w, Show_CMDLIN)) {
2163
Frames_libflags |= L_CMDLINE;
2164
// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln;
2167
s = scat(s, h+advance);
2168
Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
2170
if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
2172
if (Rc.mode_altscr) w = w->next;
2173
} while (w != Curwin);
2175
// do we need the kernel symbol table (and is it already open?)
2177
if (No_ksyms == -1) {
2179
if (open_psdb_message(NULL, msg_save))
2186
if (selection_type=='U') Frames_libflags |= L_status;
2188
if (Frames_libflags & L_EITHER) {
2189
Frames_libflags &= ~L_EITHER;
2190
if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
2192
if (!Frames_libflags) Frames_libflags = L_DEFAULT;
2193
if (selection_type=='p') Frames_libflags |= PROC_PID;
2197
// Value a window's name and make the associated group name.
2198
static void win_names (WIN_t *q, const char *name)
2200
sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
2201
sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
2205
// Display a window/field group (ie. make it "current").
2206
static void win_select (char ch)
2208
static const char prompt[] = "Choose field group (1 - 4)";
2210
/* if there's no ch, it means we're supporting the external interface,
2211
so we must try to get our own darn ch by begging the user... */
2214
chin(0, (char *)&ch, 1);
2217
case 'a': /* we don't carry 'a' / 'w' in our */
2218
Curwin = Curwin->next; /* pmt - they're here for a good */
2219
break; /* friend of ours -- wins_colors. */
2220
case 'w': /* (however those letters work via */
2221
Curwin = Curwin->prev; /* the pmt too but gee, end-loser */
2222
break; /* should just press the darn key) */
2225
Curwin = &Winstk[ch - '1'];
2231
// Just warn the user when a command can't be honored.
2232
static int win_warn (void)
2234
show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname));
2235
// we gotta' return false 'cause we're somewhat well known within
2236
// macro society, by way of that sassy little tertiary operator...
2241
// Change colors *Helper* function to save/restore settings;
2242
// ensure colors will show; and rebuild the terminfo strings.
2243
static void winsclrhlp (WIN_t *q, int save)
2245
static int flgssav, summsav, msgssav, headsav, tasksav;
2248
flgssav = q->rc.winflags; summsav = q->rc.summclr;
2249
msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr;
2250
SETw(q, Show_COLORS);
2252
q->rc.winflags = flgssav; q->rc.summclr = summsav;
2253
q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav;
2259
// Change colors used in display
2260
static void wins_colors (void)
2262
#define kbdABORT 'q'
2263
#define kbdAPPLY '\n'
2264
int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
2267
if (0 >= max_colors) {
2268
show_msg("\aNo colors to map!");
2271
winsclrhlp(Curwin, 1);
2273
putp(Cap_curs_huge);
2277
/* this string is well above ISO C89's minimum requirements! */
2284
CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
2285
CHKw(Curwin, Show_COLORS) ? "On" : "Off",
2286
CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
2295
pclr = &Curwin->rc.summclr;
2300
pclr = &Curwin->rc.msgsclr;
2305
pclr = &Curwin->rc.headclr;
2310
pclr = &Curwin->rc.taskclr;
2319
TOGw(Curwin, View_NOBOLD);
2322
TOGw(Curwin, Show_HIBOLD);
2325
TOGw(Curwin, Show_COLORS);
2330
winsclrhlp(Curwin, 1);
2331
clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
2336
} while (kbdAPPLY != ch && kbdABORT != ch);
2339
winsclrhlp(Curwin, 0);
2340
putp(Cap_curs_norm);
2347
// Manipulate flag(s) for all our windows.
2348
static void wins_reflag (int what, int flg)
2358
case Flags_SET: /* Ummmm, i can't find anybody */
2359
SETw(w, flg); /* who uses Flags_set ... */
2365
// a flag with special significance -- user wants to rebalance
2366
// display so we gotta' 'off' one number then force on two flags...
2367
if (EQUWINS_cwo == flg) {
2369
SETw(w, Show_IDLEPS | VISIBLE_tsk);
2372
} while (w != Curwin);
2376
// using a flag to avoid other code seeing inconsistant state
2377
static volatile int need_resize;
2378
static void wins_resize_sighandler (int dont_care_sig)
2380
(void)dont_care_sig;
2386
// Set the screen dimensions and arrange for the real workhorse.
2388
// SIGWINCH and SIGCONT
2389
static void wins_resize (void)
2392
char *env_columns; // Unix98 environment variable COLUMNS
2393
char *env_lines; // Unix98 environment variable LINES
2395
Screen_cols = columns; // <term.h>
2396
Screen_rows = lines; // <term.h>
2398
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) {
2399
Screen_cols = wz.ws_col;
2400
Screen_rows = wz.ws_row;
2403
if (Batch) Screen_rows = MAXINT;
2405
env_columns = getenv("COLUMNS");
2406
if(env_columns && *env_columns){
2409
t = strtol(env_columns, &endptr, 0);
2410
if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
2412
env_lines = getenv("LINES");
2413
if(env_lines && *env_lines){
2416
t = strtol(env_lines, &endptr, 0);
2417
if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
2420
// be crudely tolerant of crude tty emulators
2421
if (avoid_last_column) Screen_cols--;
2423
// we might disappoint some folks (but they'll deserve it)
2424
if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
2426
// keep our support for output optimization in sync with current reality
2427
// note: when we're in Batch mode, we don't really need a Pseudo_scrn and
2428
// when not Batch, our buffer will contain 1 extra 'line' since
2429
// Msg_row is never represented -- but it's nice to have some space
2430
// between us and the great-beyond...
2431
Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
2432
if (Batch) Pseudo_size = ROWBUFSIZ + 1;
2433
else Pseudo_size = Pseudo_cols * Screen_rows;
2434
Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size);
2436
// force rebuild of column headers AND libproc/readproc requirements
2437
Frames_libflags = 0;
2441
// Set up the raw/incomplete field group windows --
2442
// they'll be finished off after startup completes.
2443
// [ and very likely that will override most/all of our efforts ]
2444
// [ --- life-is-NOT-fair --- ]
2445
static void windows_stage1 (void)
2450
for (i = 0; i < GROUPSMAX; i++) {
2454
w->captab[0] = Cap_norm;
2455
w->captab[1] = Cap_norm;
2456
w->captab[2] = w->cap_bold;
2457
w->captab[3] = w->capclr_sum;
2458
w->captab[4] = w->capclr_msg;
2459
w->captab[5] = w->capclr_pmt;
2460
w->captab[6] = w->capclr_hdr;
2461
w->captab[7] = w->capclr_rowhigh;
2462
w->captab[8] = w->capclr_rownorm;
2467
/* fixup the circular chains... */
2468
Winstk[3].next = &Winstk[0];
2469
Winstk[0].prev = &Winstk[3];
2474
// This guy just completes the field group windows after the
2475
// rcfiles have been read and command line arguments parsed
2476
static void windows_stage2 (void)
2480
for (i = 0; i < GROUPSMAX; i++) {
2481
win_names(&Winstk[i], Winstk[i].rc.winname);
2484
// rely on this next guy to force a call (eventually) to reframewins
2489
/*###### Main Screen routines ##########################################*/
2491
// Process keyboard input during the main loop
2492
static void do_key (unsigned c)
2494
// standardized 'secure mode' errors
2495
static const char err_secure[] = "\aUnavailable in secure mode";
2496
static const char err_num_cpus[] = "\aSorry, terminal is not big enough";
2498
// standardized 'smp' errors
2499
static const char err_smp[] = "\aSorry, only 1 cpu detected";
2504
if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) {
2505
show_msg(err_num_cpus);
2509
if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
2510
else show_msg(err_smp);
2512
TOGw(Curwin, View_CPUSUM);
2517
if (Rc.mode_altscr) Curwin = Curwin->next;
2521
Rc.mode_altscr = !Rc.mode_altscr;
2527
if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
2528
show_msg("\aNothing to highlight!");
2530
TOGw(Curwin, Show_HIBOLD);
2537
TOGw(Curwin, View_NOBOLD);
2542
VIZTOGc(Show_CMDLIN);
2548
show_msg(err_secure);
2551
get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
2552
if (tmp > -1) Rc.delay_time = tmp;
2557
if (VIZCHKc) fields_toggle();
2562
if (VIZCHKc) fields_sort();
2566
if (Rc.mode_altscr) {
2567
char tmp[GETBUFSIZ];
2568
strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname)));
2569
if (tmp[0]) win_names(Curwin, tmp);
2579
TOGw(Curwin, Show_THREADS);
2580
if(Rc.mode_irixps && smp_num_cpus>1 &&
2581
!(CHKw(Curwin, Show_THREADS))){
2582
// good for 100 CPUs per process
2583
pcpu_max_value = 9999.0;
2584
Fieldstab[P_CPU].fmts = " %4.0f";
2586
pcpu_max_value = 99.9;
2587
Fieldstab[P_CPU].fmts = " %#4.1f";
2589
show_msg(fmtmk("Show threads %s"
2590
, CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
2598
putp(Cap_curs_huge);
2599
/* this string is well above ISO C89's minimum requirements! */
2606
CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
2608
Secure_mode ? "On" : "Off",
2609
Secure_mode ? "" : KEYS_help_unsecured
2613
if ('?' == ch || 'h' == ch) {
2616
show_special(1, fmtmk(WINDOWS_help
2618
, Winstk[0].rc.winname
2619
, Winstk[1].rc.winname
2620
, Winstk[2].rc.winname
2621
, Winstk[3].rc.winname));
2624
} while ('\n' != ch);
2626
putp(Cap_curs_norm);
2631
VIZTOGc(Show_IDLEPS);
2637
Rc.mode_irixps = !Rc.mode_irixps;
2638
show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2642
Rc.mode_irixps = !Rc.mode_irixps;
2643
show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2645
if(Rc.mode_irixps && smp_num_cpus>1 &&
2646
!(CHKw(Curwin, Show_THREADS))){
2647
// good for 100 CPUs per process
2648
pcpu_max_value = 9999.0;
2649
Fieldstab[P_CPU].fmts = " %4.0f";
2651
pcpu_max_value = 99.9;
2652
Fieldstab[P_CPU].fmts = " %#4.1f";
2658
show_msg(err_secure);
2660
int sig, pid = get_int("PID to kill");
2662
sig = signal_name_to_number(
2663
ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL)));
2664
if (sig == -1) sig = DEF_SIGNAL;
2665
if (sig && kill(pid, sig))
2666
show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno)));
2672
TOGw(Curwin, View_LOADAV);
2676
TOGw(Curwin, View_MEMORY);
2683
get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
2684
if (num > -1) Curwin->rc.maxtasks = num;
2689
if (VIZCHKc) fields_reorder();
2697
show_msg(err_secure);
2699
int val, pid = get_int("PID to renice");
2701
val = get_int(fmtmk("Renice PID %d to value", pid));
2702
if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
2703
show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno)));
2709
VIZTOGc(Qsrt_NORMAL);
2714
TOGw(Curwin, Show_CTIMES);
2715
show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
2720
TOGw(Curwin, View_STATES);
2725
// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
2729
// if (!VIZCHKc) break;
2733
answer = ask4str("Which user (blank for all)");
2734
// FIXME: do this better:
2735
if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
2740
errmsg = parse_uid(answer, &selection_uid);
2743
// Change settings here? I guess not.
2746
selection_type = 'u';
2751
// if (!VIZCHKc) break;
2755
answer = ask4str("Which user (blank for all)");
2756
// FIXME: do this better:
2757
if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
2762
errmsg = parse_uid(answer, &selection_uid);
2765
// Change settings here? I guess not.
2768
selection_type = 'U';
2773
if (Rc.mode_altscr) Curwin = Curwin->prev;
2777
{ const char *err = rc_write_whatever();
2779
show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
2781
show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
2787
TOGw(Curwin, Show_HICOLS);
2794
TOGw(Curwin, Show_HIROWS);
2801
TOGw(Curwin, Show_COLORS);
2811
if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
2815
if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
2819
Curwin->rc.maxtasks = 0;
2820
SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
2822
selection_type = '\0';
2826
if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
2831
FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1;
2832
while (*p != Curwin->rc.sortindx) --p;
2833
if (--p >= Curwin->procflags)
2834
Curwin->rc.sortindx = *p;
2840
FLG_t *p = Curwin->procflags;
2841
while (*p != Curwin->rc.sortindx) ++p;
2842
if (++p < Curwin->procflags + Curwin->maxpflgs)
2843
Curwin->rc.sortindx = *p;
2847
case 'M': // these keys represent old-top compatability
2848
case 'N': // -- grouped here so that if users could ever
2849
case 'P': // be weaned, we would just whack this part...
2853
const unsigned xkey;
2856
{ "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
2857
{ "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, };
2859
for (i = 0; i < MAXTBL(xtab); ++i)
2860
if (c == xtab[i].xkey) {
2861
Curwin->rc.sortindx = xtab[i].sort;
2862
// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg));
2868
case '\n': // just ignore these, they'll have the effect
2869
case ' ': // of refreshing display after waking us up !
2873
show_msg("\aUnknown command - try 'h' for help");
2875
// The following assignment will force a rebuild of all column headers and
2876
// the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are
2877
// some keys that COULD require new column headers and/or libproc flags:
2879
// 'c' - likely when !Mode_altscr, maybe when Mode_altscr
2880
// 'F' - maybe, if new field forced on
2883
// 'O' - maybe, if new field forced on
2884
// 'o' - maybe, if new field brought into view
2885
// 'Z' - likely, if 'Curwin' changed when !Mode_altscr
2886
// '-' - likely (restricted to Mode_altscr)
2887
// '_' - likely (restricted to Mode_altscr)
2888
// '=' - maybe, but only when Mode_altscr
2889
// '+' - likely (restricted to Mode_altscr)
2890
// ( At this point we have a human being involved and so have all the time )
2891
// ( in the world. We can afford a few extra cpu cycles every now & then! )
2892
Frames_libflags = 0;
2896
// State display *Helper* function to calc and display the state
2897
// percentages for a single cpu. In this way, we can support
2898
// the following environments without the usual code bloat.
2899
// 1) single cpu machines
2900
// 2) modest smp boxes with room for each cpu's percentages
2901
// 3) massive smp guys leaving little or no room for process
2902
// display and thus requiring the cpu summary toggle
2903
static void summaryhlp (CPU_t *cpu, const char *pfx)
2905
// we'll trim to zero if we get negative time ticks,
2906
// which has happened with some SMP kernels (pre-2.4?)
2907
#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
2908
SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
2911
u_frme = cpu->u - cpu->u_sav;
2912
s_frme = cpu->s - cpu->s_sav;
2913
n_frme = cpu->n - cpu->n_sav;
2914
i_frme = TRIMz(cpu->i - cpu->i_sav);
2915
w_frme = cpu->w - cpu->w_sav;
2916
x_frme = cpu->x - cpu->x_sav;
2917
y_frme = cpu->y - cpu->y_sav;
2918
z_frme = cpu->z - cpu->z_sav;
2919
tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
2920
if (tot_frme < 1) tot_frme = 1;
2921
scale = 100.0 / (float)tot_frme;
2923
// display some kinda' cpu state percentages
2924
// (who or what is explained by the passed prefix)
2930
(float)u_frme * scale,
2931
(float)s_frme * scale,
2932
(float)n_frme * scale,
2933
(float)i_frme * scale,
2934
(float)w_frme * scale,
2935
(float)x_frme * scale,
2936
(float)y_frme * scale,
2937
(float)z_frme * scale
2942
// remember for next time around
2943
cpu->u_sav = cpu->u;
2944
cpu->s_sav = cpu->s;
2945
cpu->n_sav = cpu->n;
2946
cpu->i_sav = cpu->i;
2947
cpu->w_sav = cpu->w;
2948
cpu->x_sav = cpu->x;
2949
cpu->y_sav = cpu->y;
2950
cpu->z_sav = cpu->z;
2956
// Begin a new frame by:
2957
// 1) Refreshing the all important proc table
2958
// 2) Displaying uptime and load average (maybe)
2959
// 3) Displaying task/cpu states (maybe)
2960
// 4) Displaying memory & swap usage (maybe)
2961
// and then, returning a pointer to the pointers to the proc_t's!
2962
static proc_t **summary_show (void)
2964
static proc_t **p_table = NULL;
2965
static CPU_t *smpcpu = NULL;
2967
// whoa first time, gotta' prime the pump...
2969
p_table = procs_refresh(NULL, Frames_libflags);
2973
// sleep for half a second
2975
tv.tv_usec = 500000;
2976
select(0, NULL, NULL, NULL, &tv); // ought to loop until done
2979
putp(Batch ? "\n\n" : Cap_home);
2981
p_table = procs_refresh(p_table, Frames_libflags);
2983
// Display Uptime and Loadavg
2984
if (CHKw(Curwin, View_LOADAV)) {
2985
if (!Rc.mode_altscr) {
2986
show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));
2991
CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
3000
// Display Task and Cpu(s) States
3001
if (CHKw(Curwin, View_STATES)) {
3006
Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
3011
smpcpu = cpus_refresh(smpcpu);
3013
if (CHKw(Curwin, View_CPUSUM)) {
3014
// display just the 1st /proc/stat line
3015
summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
3018
char tmp[SMLBUFSIZ];
3019
// display each cpu's states separately
3020
for (i = 0; i < Cpu_tot; i++) {
3021
snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);
3022
summaryhlp(&smpcpu[i], tmp);
3027
// Display Memory and Swap stats
3029
if (CHKw(Curwin, View_MEMORY)) {
3030
show_special(0, fmtmk(MEMORY_line1
3031
, kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));
3032
show_special(0, fmtmk(MEMORY_line2
3033
, kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));
3037
SETw(Curwin, NEWFRAM_cwo);
3042
#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift )
3044
// the following macro is our means to 'inline' emitting a column -- next to
3045
// procs_refresh, that's the most frequent and costly part of top's job !
3046
#define MKCOL(va...) do { \
3047
if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \
3048
snprintf(cbuf, sizeof(cbuf), f, ## va); \
3050
snprintf(_z, sizeof(_z), f, ## va); \
3051
snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
3052
q->capclr_rowhigh, \
3054
!(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
3056
pad += q->len_rowhigh; \
3057
if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
3061
// Display information for a single task row.
3062
static void task_show (const WIN_t *q, const proc_t *p)
3064
char rbuf[ROWBUFSIZ];
3070
pad = Rc.mode_altscr;
3071
// if (pad) rp = scat(rp, " ");
3073
for (x = 0; x < q->maxpflgs; x++) {
3074
char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ];
3075
FLG_t i = q->procflags[x]; // support for our field/column
3076
const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt
3077
unsigned s = Fieldstab[i].scale; // string must be altered !
3078
unsigned w = Fieldstab[i].width;
3080
int advance = (x==0) && !Rc.mode_altscr;
3084
{ char tmp[ROWBUFSIZ];
3086
int maxcmd = q->maxcmdln;
3087
if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
3088
else flags = ESC_DEFUNCT;
3089
escape_command(tmp, p, sizeof tmp, &maxcmd, flags);
3090
MKCOL(q->maxcmdln, q->maxcmdln, tmp);
3094
MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
3097
MKCOL((unsigned)p->processor);
3100
{ float u = (float)p->pcpu * Frame_tscale;
3101
if (u > pcpu_max_value) u = pcpu_max_value;
3106
MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
3109
MKCOL(scale_num((unsigned)p->dt, w, s));
3112
{ char tmp[TNYBUFSIZ];
3113
snprintf(tmp, sizeof(tmp), f, (long)p->flags);
3114
for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.';
3120
MKCOL(scale_num(p->maj_flt, w, s));
3126
MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
3129
MKCOL((int)p->nice);
3132
MKCOL((unsigned)p->XXXID);
3135
MKCOL((unsigned)p->ppid);
3138
if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
3142
MKCOL((int)p->priority);
3145
MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
3148
MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
3154
MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
3158
{ TIC_t t = p->utime + p->stime;
3159
if (CHKw(q, Show_CTIMES))
3160
t += (p->cutime + p->cstime);
3161
MKCOL(scale_tics(t, w));
3165
{ char tmp[TNYBUFSIZ];
3166
dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
3171
MKCOL((unsigned)p->euid);
3180
MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
3185
MKCOL((long)p->wchan);
3187
MKCOL(lookup_wchan(p->wchan, p->XXXID));
3191
} /* end: switch 'procflag' */
3193
rp = scat(rp, cbuf+advance);
3194
} /* end: for 'maxpflgs' */
3198
(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
3209
// Squeeze as many tasks as we can into a single window,
3210
// after sorting the passed proc table.
3211
static void window_show (proc_t **ppt, WIN_t *q, int *lscr)
3214
// the 1 flag that DOES and 2 flags that MAY impact our proc table qsort
3215
#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES )
3216
static FLG_t sav_indx = 0;
3217
static int sav_flgs = -1;
3221
// Display Column Headings -- and distract 'em while we sort (maybe)
3222
PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol);
3225
if (CHKw(Curwin, NEWFRAM_cwo)
3226
|| sav_indx != q->rc.sortindx
3227
|| sav_flgs != (q->rc.winflags & srtMASK)) {
3228
sav_indx = q->rc.sortindx;
3229
sav_flgs = (q->rc.winflags & srtMASK);
3231
if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed!
3232
else Frame_srtflg = -1;
3233
Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe
3234
Frame_cmdlin = CHKw(q, Show_CMDLIN);
3235
qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);
3239
// account for column headings
3244
while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) {
3245
if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state))
3246
&& good_uid(ppt[i]) ) {
3247
// Display a process Row
3248
task_show(q, ppt[i]);
3254
// for this frame that window's toast, cleanup for next time
3256
OFFw(Curwin, FLGSOFF_cwo);
3264
/*###### Entry point plus two ##########################################*/
3266
// This guy's just a *Helper* function who apportions the
3267
// remaining amount of screen real estate under multiple windows
3268
static void framehlp (int wix, int max)
3270
int i, rsvd, size, wins;
3272
// calc remaining number of visible windows + total 'user' lines
3273
for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) {
3274
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3275
rsvd += Winstk[i].rc.maxtasks;
3277
if (max <= rsvd) break;
3280
if (!wins) wins = 1;
3281
// set aside 'rsvd' & deduct 1 line/window for the columns heading
3282
size = (max - wins) - rsvd;
3283
if (0 <= size) size = max;
3284
size = (max - wins) / wins;
3286
// for remaining windows, set WIN_t winlines to either the user's
3287
// maxtask (1st choice) or our 'foxized' size calculation
3288
// (foxized adj. - 'fair and balanced')
3289
for (i = wix ; i < GROUPSMAX; i++) {
3290
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3291
Winstk[i].winlines =
3292
Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size;
3298
// Initiate the Frame Display Update cycle at someone's whim!
3299
// This routine doesn't do much, mostly he just calls others.
3301
// (Whoa, wait a minute, we DO caretake those row guys, plus)
3302
// (we CALCULATE that IMPORTANT Max_lines thingy so that the)
3303
// (*subordinate* functions invoked know WHEN the user's had)
3304
// (ENOUGH already. And at Frame End, it SHOULD be apparent)
3305
// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring)
3306
// (the CURSOR is STUCK in just the RIGHT place, know what I)
3307
// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say)
3308
// (THAT about THIS function again, Ok? Good that's better.)
3309
static void frame_make (void)
3314
// note: all libproc flags are managed by
3315
// reframewins(), who also builds each window's column headers
3316
if (!Frames_libflags) {
3318
memset(Pseudo_scrn, '\0', Pseudo_size);
3320
Pseudo_row = Msg_row = scrlins = 0;
3321
ppt = summary_show();
3322
Max_lines = (Screen_rows - Msg_row) - 1;
3324
if (CHKw(Curwin, EQUWINS_cwo))
3325
wins_reflag(Flags_OFF, EQUWINS_cwo);
3327
// sure hope each window's columns header begins with a newline...
3328
putp(tg2(0, Msg_row));
3330
if (!Rc.mode_altscr) {
3331
// only 1 window to show so, piece o' cake
3332
Curwin->winlines = Curwin->rc.maxtasks;
3333
window_show(ppt, Curwin, &scrlins);
3335
// maybe NO window is visible but assume, pieces o' cakes
3336
for (i = 0 ; i < GROUPSMAX; i++) {
3337
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
3338
framehlp(i, Max_lines - scrlins);
3339
window_show(ppt, &Winstk[i], &scrlins);
3341
if (Max_lines <= scrlins) break;
3344
// clear to end-of-screen (critical if last window is 'idleps off'),
3345
// then put the cursor in-its-place, and rid us of any prior frame's msg
3346
// (main loop must iterate such that we're always called before sleep)
3349
scrlins < Max_lines ? "\n" : "",
3350
scrlins < Max_lines ? Cap_clr_eos : "",
3358
int main (int dont_care_argc, char *argv[])
3360
(void)dont_care_argc;
3363
windows_stage1(); // top (sic) slice
3364
configs_read(); // > spread etc, <
3365
parse_args(&argv[1]); // > lean stuff, <
3366
whack_terminal(); // > onions etc. <
3367
windows_stage2(); // as bottom slice
3369
signal(SIGALRM, end_pgm);
3370
signal(SIGHUP, end_pgm);
3371
signal(SIGINT, end_pgm);
3372
signal(SIGPIPE, end_pgm);
3373
signal(SIGQUIT, end_pgm);
3374
signal(SIGTERM, end_pgm);
3375
signal(SIGTSTP, suspend);
3376
signal(SIGTTIN, suspend);
3377
signal(SIGTTOU, suspend);
3378
signal(SIGCONT, wins_resize_sighandler);
3379
signal(SIGWINCH, wins_resize_sighandler);
3388
if (Msg_awaiting) show_msg(Msg_delayed);
3389
if (Loops > 0) --Loops;
3390
if (!Loops) end_pgm(0);
3392
tv.tv_sec = Rc.delay_time;
3393
tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
3396
select(0, NULL, NULL, NULL, &tv); // ought to loop until done
3403
FD_SET(STDIN_FILENO, &fs);
3404
file_flags = fcntl(STDIN_FILENO, F_GETFL);
3405
if(file_flags==-1) file_flags=0;
3406
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
3408
// check 1st, in case tv zeroed (by sig handler) before it got set
3409
rc = chin(0, &c, 1);
3411
if (rc == 0) end_pgm(0); /* EOF from terminal, may happen if top
3412
* erroneously gets detached from it. */
3413
fcntl(STDIN_FILENO, F_SETFL, file_flags);
3414
select(1, &fs, NULL, NULL, &tv);
3415
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
3417
if (chin(0, &c, 1) > 0) {
3418
fcntl(STDIN_FILENO, F_SETFL, file_flags);
3419
do_key((unsigned)c);
3421
fcntl(STDIN_FILENO, F_SETFL, file_flags);