procps/top.c

3368 lines
108 KiB
C
Raw Normal View History

2002-05-30 09:14:46 +05:30
/* top.c - Source file: show Linux processes */
2002-02-02 04:17:29 +05:30
/*
2002-06-27 05:26:49 +05:30
* Copyright (c) 2002, by: James C. Warner
2002-06-19 05:15:30 +05:30
* All rights reserved. 8921 Hilloway Road
* Eden Prairie, Minnesota 55347 USA
* <warnerjc@worldnet.att.net>
*
2002-05-30 09:14:46 +05:30
* This file may be used subject to the terms and conditions of the
* GNU Library General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* For their contributions to this program, the author wishes to thank:
* Albert D. Cahalan, <albert@users.sf.net>
2002-12-06 12:23:29 +05:30
* Craig Small, <csmall@small.dropbear.id.au>
*
2004-07-07 01:35:30 +05:30
* Changes by Albert Cahalan, 2002-2004.
2002-06-19 05:15:30 +05:30
*/
2002-05-30 09:14:46 +05:30
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/time.h>
2002-11-30 21:26:53 +05:30
#include <sys/types.h>
#include <sys/stat.h>
2002-05-30 09:14:46 +05:30
#include <ctype.h>
#include <curses.h>
2002-02-02 04:17:29 +05:30
#include <errno.h>
2002-12-05 04:18:30 +05:30
#include <fcntl.h>
2002-05-30 09:14:46 +05:30
#include <signal.h>
#include <stdarg.h>
2002-02-02 04:17:29 +05:30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2003-10-21 06:21:36 +05:30
// Foul POS defines all sorts of stuff...
2002-05-30 09:14:46 +05:30
#include <term.h>
2003-10-21 06:21:36 +05:30
#undef tab
2002-02-02 04:17:29 +05:30
#include <termios.h>
2002-05-30 09:14:46 +05:30
#include <time.h>
#include <unistd.h>
#include <values.h>
2002-11-08 06:01:28 +05:30
2002-05-30 09:14:46 +05:30
#include "proc/devname.h"
2002-12-09 12:30:07 +05:30
#include "proc/wchan.h"
2002-02-02 04:17:29 +05:30
#include "proc/procps.h"
#include "proc/readproc.h"
#include "proc/escape.h"
2002-05-30 09:14:46 +05:30
#include "proc/sig.h"
#include "proc/sysinfo.h"
#include "proc/version.h"
#include "proc/whattime.h"
2002-02-02 04:17:29 +05:30
2002-05-30 09:14:46 +05:30
#include "top.h"
/*###### Miscellaneous global stuff ####################################*/
/* The original and new terminal attributes */
static struct termios Savedtty,
Rawtty;
2002-12-05 04:18:30 +05:30
static int Ttychanged = 0;
2002-05-30 09:14:46 +05:30
2002-08-26 06:25:30 +05:30
/* Program name used in error messages and local 'rc' file name */
2002-06-19 05:15:30 +05:30
static char *Myname;
2002-05-30 09:14:46 +05:30
2002-12-05 04:18:30 +05:30
/* Name of user config file (dynamically constructed) and our
'Current' rcfile contents, initialized with defaults but may be
overridden with the local rcfile (old or new-style) values */
static char Rc_name [OURPATHSZ];
static RCF_t Rc = DEF_RCFILE;
2002-05-30 09:14:46 +05:30
/* The run-time acquired page size */
2003-09-06 10:41:04 +05:30
static unsigned Page_size;
static unsigned page_to_kb_shift;
2002-05-30 09:14:46 +05:30
2004-07-07 05:41:19 +05:30
/* SMP Irix/Solaris mode */
static int Cpu_tot;
2005-01-06 05:43:12 +05:30
static double pcpu_max_value; // usually 99.9, for %CPU display
/* assume no IO-wait stats, overridden if linux 2.5.41 */
2002-10-19 03:10:19 +05:30
static const char *States_fmts = STATES_line2x4;
2002-05-30 09:14:46 +05:30
/* Specific process id monitoring support */
2002-12-05 04:18:30 +05:30
static pid_t Monpids [MONPIDMAX] = { 0 };
static int Monpidsidx = 0;
2002-05-30 09:14:46 +05:30
/* A postponed error message */
2002-12-05 04:18:30 +05:30
static char Msg_delayed [SMLBUFSIZ];
static int Msg_awaiting = 0;
2002-05-30 09:14:46 +05:30
2003-03-18 05:12:00 +05:30
// This is the select() timeout. Clear it in sig handlers to avoid a race.
static volatile struct timeval tv;
#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0);
2002-05-30 09:14:46 +05:30
/* Configurable Display support ##################################*/
2002-06-19 05:15:30 +05:30
/* Current screen dimensions.
note: the number of processes displayed is tracked on a per window
basis (see the WIN_t). Max_lines is the total number of
screen rows after deducting summary information overhead. */
/* Current terminal screen size. */
2002-12-05 04:18:30 +05:30
static int Screen_cols, Screen_rows, Max_lines;
2002-06-19 05:15:30 +05:30
// set to 1 if writing to the last column would be troublesome
// (we don't distinguish the lowermost row from the other rows)
static int avoid_last_column;
2002-06-19 05:15:30 +05:30
/* This is really the number of lines needed to display the summary
information (0 - nn), but is used as the relative row where we
stick the cursor between frames. */
2002-12-05 04:18:30 +05:30
static int Msg_row;
2002-11-30 21:26:53 +05:30
2002-06-19 05:15:30 +05:30
/* Global/Non-windows mode stuff that is NOT persistent */
2002-12-08 04:06:21 +05:30
static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise
2002-12-05 04:18:30 +05:30
PSDBopen = 0, // set to '1' if psdb opened (now postponed)
Batch = 0, // batch mode, collect no input, dumb output
Loops = -1, // number of iterations, -1 loops forever
Secure_mode = 0; // set if some functionality restricted
2002-06-19 05:15:30 +05:30
2002-05-30 09:14:46 +05:30
/* Some cap's stuff to reduce runtime calls --
to accomodate 'Batch' mode, they begin life as empty strings */
static char Cap_clr_eol [CAPBUFSIZ],
Cap_clr_eos [CAPBUFSIZ],
Cap_clr_scr [CAPBUFSIZ],
Cap_rmam [CAPBUFSIZ],
Cap_smam [CAPBUFSIZ],
Cap_curs_norm [CAPBUFSIZ],
Cap_curs_huge [CAPBUFSIZ],
Cap_home [CAPBUFSIZ],
Cap_norm [CAPBUFSIZ],
Cap_reverse [CAPBUFSIZ],
Caps_off [CAPBUFSIZ];
2002-06-19 05:15:30 +05:30
static int Cap_can_goto = 0;
2002-05-30 09:14:46 +05:30
2002-12-05 04:18:30 +05:30
/* Some optimization stuff, to reduce output demands...
2002-11-27 05:54:01 +05:30
The Pseudo_ guys are managed by wins_resize and frame_make. They
are exploited in a macro and represent 90% of our optimization.
The Stdout_buf is transparent to our code and regardless of whose
buffer is used, stdout is flushed at frame end or if interactive. */
static char *Pseudo_scrn;
static int Pseudo_row, Pseudo_cols, Pseudo_size;
#ifndef STDOUT_IOLBF
2002-11-27 05:54:01 +05:30
// less than stdout's normal buffer but with luck mostly '\n' anyway
static char Stdout_buf[2048];
#endif
2002-06-19 05:15:30 +05:30
/* ////////////////////////////////////////////////////////////// */
/* Special Section: multiple windows/field groups ---------------*/
/* The pointers to our four WIN_t's, and which of those is considered
the 'current' window (ie. which window is associated with any summ
info displayed and to which window commands are directed) */
2002-12-05 08:30:42 +05:30
static WIN_t Winstk [GROUPSMAX],
2002-06-19 05:15:30 +05:30
*Curwin;
/* Frame oriented stuff that can't remain local to any 1 function
and/or that would be too cumbersome managed as parms,
2002-12-05 04:18:30 +05:30
and/or that are simply more efficiently handled as globals
(first 2 persist beyond a single frame, changed infrequently) */
2002-12-08 04:06:21 +05:30
static int Frames_libflags; // PROC_FILLxxx flags (0 = need new)
//atic int Frames_maxcmdln; // the largest from the 4 windows
static unsigned Frame_maxtask; // last known number of active tasks
// ie. current 'size' of proc table
static unsigned Frame_running, // state categories for this frame
2002-11-08 06:01:28 +05:30
Frame_sleepin,
Frame_stopped,
Frame_zombied;
2002-12-08 04:06:21 +05:30
static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu'
static int Frame_srtflg, // the subject window's sort direction
Frame_ctimes, // the subject window's ctimes flag
Frame_cmdlin; // the subject window's cmdlin flag
2002-06-19 05:15:30 +05:30
/* ////////////////////////////////////////////////////////////// */
2002-05-30 09:14:46 +05:30
/*###### Sort callbacks ################################################*/
2002-10-02 05:40:30 +05:30
/*
* These happen to be coded in the same order as the enum 'pflag'
2002-10-07 01:42:08 +05:30
* values. Note that 2 of these routines serve double duty --
* 2 columns each.
2002-10-02 05:40:30 +05:30
*/
2002-12-06 12:23:29 +05:30
2003-09-20 13:59:55 +05:30
SCB_NUMx(P_PID, XXXID)
2002-12-05 04:18:30 +05:30
SCB_NUMx(P_PPD, ppid)
2002-12-06 12:23:29 +05:30
SCB_STRx(P_URR, ruser)
2002-12-05 04:18:30 +05:30
SCB_NUMx(P_UID, euid)
2002-12-06 12:23:29 +05:30
SCB_STRx(P_URE, euser)
2002-12-05 04:18:30 +05:30
SCB_STRx(P_GRP, egroup)
SCB_NUMx(P_TTY, tty)
SCB_NUMx(P_PRI, priority)
SCB_NUMx(P_NCE, nice)
SCB_NUMx(P_CPN, processor)
SCB_NUM1(P_CPU, pcpu)
2002-11-08 06:01:28 +05:30
// also serves P_TM2 !
2002-09-13 17:12:44 +05:30
static int sort_P_TME (const proc_t **P, const proc_t **Q)
2002-06-19 05:15:30 +05:30
{
2002-09-13 17:12:44 +05:30
if (Frame_ctimes) {
if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
< ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
return SORT_lt;
if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
> ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
return SORT_gt;
} else {
if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
return SORT_lt;
if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
return SORT_gt;
}
return SORT_eq;
2002-06-19 05:15:30 +05:30
}
2002-12-05 04:18:30 +05:30
SCB_NUM1(P_VRT, size)
SCB_NUM2(P_SWP, size, resident)
SCB_NUM1(P_RES, resident) // also serves P_MEM !
SCB_NUM1(P_COD, trs)
SCB_NUM1(P_DAT, drs)
SCB_NUM1(P_SHR, share)
SCB_NUM1(P_FLT, maj_flt)
SCB_NUM1(P_DRT, dt)
SCB_NUMx(P_STA, state)
2002-09-13 17:12:44 +05:30
static int sort_P_CMD (const proc_t **P, const proc_t **Q)
2002-05-30 09:14:46 +05:30
{
/* if a process doesn't have a cmdline, we'll consider it a kernel thread
2002-11-08 06:01:28 +05:30
-- since displayed tasks are given special treatment, we must too */
2002-06-19 05:15:30 +05:30
if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
2002-10-07 01:42:08 +05:30
if (!(*Q)->cmdline) return Frame_srtflg * -1;
if (!(*P)->cmdline) return Frame_srtflg;
return Frame_srtflg *
strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln);
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
// this part also handles the compare if both are kernel threads
2002-10-07 01:42:08 +05:30
return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
2002-05-30 09:14:46 +05:30
}
2002-02-02 04:17:29 +05:30
2002-12-05 04:18:30 +05:30
SCB_NUM1(P_WCH, wchan)
SCB_NUM1(P_FLG, flags)
2002-02-02 04:17:29 +05:30
/* ///////////////////////////////// special sort for prochlp() ! */
2002-12-05 04:18:30 +05:30
static int sort_HST_t (const HST_t *P, const HST_t *Q)
2002-11-08 06:01:28 +05:30
{
2002-12-09 04:21:09 +05:30
return P->pid - Q->pid;
2002-11-08 06:01:28 +05:30
}
2002-12-15 03:00:58 +05:30
2002-05-30 09:14:46 +05:30
/*###### Tiny useful routine(s) ########################################*/
/*
* This routine isolates ALL user INPUT and ensures that we
2002-05-30 09:14:46 +05:30
* wont be mixing I/O from stdio and low-level read() requests */
static int chin (int ech, char *buf, unsigned cnt)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
int rc;
fflush(stdout);
if (!ech)
rc = read(STDIN_FILENO, buf, cnt);
else {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
rc = read(STDIN_FILENO, buf, cnt);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
}
2004-07-07 22:57:26 +05:30
// may be the beginning of a lengthy escape sequence
2002-05-30 09:14:46 +05:30
tcflush(STDIN_FILENO, TCIFLUSH);
2004-07-07 22:57:26 +05:30
return rc; // note: we do NOT produce a vaid 'string'
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
2004-07-07 22:57:26 +05:30
// This routine simply formats whatever the caller wants and
// returns a pointer to the resulting 'const char' string...
2002-05-30 09:14:46 +05:30
static const char *fmtmk (const char *fmts, ...)
2002-02-02 04:17:29 +05:30
{
2004-07-07 22:57:26 +05:30
static char buf[BIGBUFSIZ]; // with help stuff, our buffer
va_list va; // requirements exceed 1k
2002-05-30 09:14:46 +05:30
va_start(va, fmts);
2002-09-13 17:12:44 +05:30
vsnprintf(buf, sizeof(buf), fmts, va);
2002-05-30 09:14:46 +05:30
va_end(va);
return (const char *)buf;
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
2004-07-07 22:57:26 +05:30
// This guy is just our way of avoiding the overhead of the standard
// strcat function (should the caller choose to participate)
2002-11-25 15:46:33 +05:30
static inline char *scat (char *restrict dst, const char *restrict src)
{
while (*dst) dst++;
while ((*(dst++) = *(src++)));
return --dst;
}
// Trim the rc file lines and any 'open_psdb_message' result which arrives
// with an inappropriate newline (thanks to 'sysmap_mmap')
2002-12-09 04:21:09 +05:30
static char *strim_0 (char *str)
2002-05-30 09:14:46 +05:30
{
2002-12-14 09:53:30 +05:30
static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape
2002-05-30 09:14:46 +05:30
char *p;
2002-12-09 04:21:09 +05:30
if ((p = strpbrk(str, ws))) *p = 0;
return str;
}
2002-02-02 04:17:29 +05:30
2004-07-07 22:57:26 +05:30
// This guy just facilitates Batch and protects against dumb ttys
// -- we'd 'inline' him but he's only called twice per frame,
// yet used in many other locations.
2002-10-12 09:55:57 +05:30
static const char *tg2 (int x, int y)
2002-02-02 04:17:29 +05:30
{
2002-10-02 05:40:30 +05:30
return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
2002-06-19 05:15:30 +05:30
}
2002-12-15 03:00:58 +05:30
2002-06-19 05:15:30 +05:30
/*###### Exit/Interrput routines #######################################*/
2004-07-07 22:57:26 +05:30
// The usual program end -- called only by functions in this section.
static void bye_bye (FILE *fp, int eno, const char *str) NORETURN;
static void bye_bye (FILE *fp, int eno, const char *str)
2002-06-19 05:15:30 +05:30
{
if (!Batch)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
putp(tg2(0, Screen_rows));
putp(Cap_curs_norm);
putp(Cap_smam);
putp("\n");
fflush(stdout);
2002-06-19 05:15:30 +05:30
2004-07-19 10:11:56 +05:30
//#define ATEOJ_REPORT
2002-06-19 05:15:30 +05:30
#ifdef ATEOJ_REPORT
fprintf(fp,
2002-10-07 01:42:08 +05:30
"\n\tTerminal: %s"
2002-06-19 05:15:30 +05:30
"\n\t device = %s, ncurses = v%s"
"\n\t max_colors = %d, max_pairs = %d"
"\n\t Cap_can_goto = %s"
"\n\t Screen_cols = %d, Screen_rows = %d"
"\n\t Max_lines = %d, most recent Pseudo_size = %d"
2002-06-19 05:15:30 +05:30
"\n"
#ifdef PRETENDNOCAP
, "dumb"
#else
, termname()
#endif
, ttyname(STDOUT_FILENO), NCURSES_VERSION
, max_colors, max_pairs
, Cap_can_goto ? "yes" : "No!"
, Screen_cols, Screen_rows
, Max_lines, Pseudo_size
);
fprintf(fp,
#ifndef STDOUT_IOLBF
"\n\t Stdout_buf = %d, BUFSIZ = %u"
#endif
"\n\tWindows and Curwin->"
"\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d"
"\n\t rc.winname = %s, grpname = %s"
"\n\t rc.winflags = %08x, maxpflgs = %d"
"\n\t rc.fieldscur = %s"
"\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d"
"\n\t rc.sortindx = %d"
"\n"
#ifndef STDOUT_IOLBF
, sizeof(Stdout_buf), (unsigned)BUFSIZ
#endif
2002-06-19 05:15:30 +05:30
, sizeof(WIN_t), GROUPSMAX
2002-11-30 06:52:01 +05:30
, Curwin->rc.winname, Curwin->grpname
, Curwin->rc.winflags, Curwin->maxpflgs
, Curwin->rc.fieldscur
, Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln
, Curwin->rc.sortindx
2002-06-19 05:15:30 +05:30
);
fprintf(fp,
2004-07-07 22:57:26 +05:30
"\n\tProgram"
"\n\t Linux version = %u.%u.%u, %s"
"\n\t Hertz = %u (%u bytes, %u-bit time)"
"\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u"
"\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)"
"\n"
, LINUX_VERSION_MAJOR(linux_version_code)
, LINUX_VERSION_MINOR(linux_version_code)
, LINUX_VERSION_PATCH(linux_version_code)
, procps_version
, (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8
, Page_size, Cpu_tot, sizeof(proc_t)
, sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t)
);
2002-06-19 05:15:30 +05:30
#endif
if (str) fputs(str, fp);
2002-06-19 05:15:30 +05:30
exit(eno);
}
/*
* Normal end of execution.
* catches:
* SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
2002-12-09 13:23:09 +05:30
static void end_pgm (int dont_care_sig) NORETURN;
static void end_pgm (int dont_care_sig)
2002-06-19 05:15:30 +05:30
{
2002-10-12 09:55:57 +05:30
(void)dont_care_sig;
bye_bye(stdout, 1, NULL);
2002-06-19 05:15:30 +05:30
}
/*
* Standard error handler to normalize the look of all err o/p */
2002-12-09 13:23:09 +05:30
static void std_err (const char *str) NORETURN;
2002-06-19 05:15:30 +05:30
static void std_err (const char *str)
{
static char buf[SMLBUFSIZ];
fflush(stdout);
/* we'll use our own buffer so callers can still use fmtmk() and, yes the
leading tab is not the standard convention, but the standard is wrong
-- OUR msg won't get lost in screen clutter, like so many others! */
snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
2002-06-19 05:15:30 +05:30
if (!Ttychanged) {
2002-12-05 04:18:30 +05:30
fprintf(stderr, "%s\n", buf);
2002-06-19 05:15:30 +05:30
exit(1);
}
/* not to worry, he'll change our exit code to 1 due to 'buf' */
bye_bye(stderr, 1, buf);
}
/*
* Standard out handler */
static void std_out (const char *str) NORETURN;
static void std_out (const char *str)
{
static char buf[SMLBUFSIZ];
fflush(stdout);
/* we'll use our own buffer so callers can still use fmtmk() and, yes the
leading tab is not the standard convention, but the standard is wrong
-- OUR msg won't get lost in screen clutter, like so many others! */
snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
if (!Ttychanged) {
fprintf(stdout, "%s\n", buf);
exit(0);
}
bye_bye(stdout, 0, buf);
2002-06-19 05:15:30 +05:30
}
/*
* Suspend ourself.
* catches:
* SIGTSTP, SIGTTIN and SIGTTOU */
static void suspend (int dont_care_sig)
{
2002-10-12 09:55:57 +05:30
(void)dont_care_sig;
2002-06-19 05:15:30 +05:30
/* reset terminal */
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
putp(tg2(0, Screen_rows));
putp(Cap_curs_norm);
putp(Cap_smam);
putp("\n");
2002-06-19 05:15:30 +05:30
fflush(stdout);
raise(SIGSTOP);
/* later, after SIGCONT... */
2003-03-18 05:12:00 +05:30
ZAP_TIMEOUT
2002-06-19 05:15:30 +05:30
if (!Batch)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
putp(Cap_clr_scr);
putp(Cap_rmam);
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
2002-10-02 05:40:30 +05:30
/*###### Misc Color/Display support ####################################*/
2002-05-30 09:14:46 +05:30
/* macro to test if a basic (non-color) capability is valid
thanks: Floyd Davidson <floyd@ptialaska.net> */
#define tIF(s) s ? s : ""
#define CAPCOPY(dst,src) src && strcpy(dst,src)
2002-05-30 09:14:46 +05:30
/*
* Make the appropriate caps/color strings and set some
* lengths which are used to distinguish twix the displayed
* columns and an actual printed row!
2002-05-30 09:14:46 +05:30
* note: we avoid the use of background color so as to maximize
* compatibility with the user's xterm settings */
2002-06-19 05:15:30 +05:30
static void capsmk (WIN_t *q)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
static int capsdone = 0;
// we must NOT disturb our 'empty' terminfo strings!
2002-05-30 09:14:46 +05:30
if (Batch) return;
// these are the unchangeable puppies, so we only do 'em once
2002-05-30 09:14:46 +05:30
if (!capsdone) {
CAPCOPY(Cap_clr_eol, clr_eol);
CAPCOPY(Cap_clr_eos, clr_eos);
CAPCOPY(Cap_clr_scr, clear_screen);
if (!eat_newline_glitch) { // we like the eat_newline_glitch
CAPCOPY(Cap_rmam, exit_am_mode);
CAPCOPY(Cap_smam, enter_am_mode);
if (!*Cap_rmam || !*Cap_smam) { // need both
*Cap_rmam = '\0';
*Cap_smam = '\0';
if (auto_right_margin) {
avoid_last_column = 1;
}
}
}
CAPCOPY(Cap_curs_huge, cursor_visible);
CAPCOPY(Cap_curs_norm, cursor_normal);
CAPCOPY(Cap_home, cursor_home);
CAPCOPY(Cap_norm, exit_attribute_mode);
CAPCOPY(Cap_reverse, enter_reverse_mode);
snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
2002-05-30 09:14:46 +05:30
if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
capsdone = 1;
}
/* the key to NO run-time costs for configurable colors -- we spend a
little time with the user now setting up our terminfo strings, and
the job's done until he/she/it has a change-of-heart */
strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode));
2002-06-19 05:15:30 +05:30
if (CHKw(q, Show_COLORS) && max_colors > 0) {
2002-11-30 06:52:01 +05:30
strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr));
snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
2002-11-30 06:52:01 +05:30
, tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
2002-11-30 06:52:01 +05:30
, tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
2002-11-30 06:52:01 +05:30
, tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
2002-11-30 06:52:01 +05:30
, Caps_off, tparm(set_a_foreground, q->rc.taskclr));
2002-05-30 09:14:46 +05:30
} else {
2002-06-19 05:15:30 +05:30
q->capclr_sum[0] = '\0';
strcpy(q->capclr_msg, Cap_reverse);
strcpy(q->capclr_pmt, q->cap_bold);
2002-06-19 05:15:30 +05:30
strcpy(q->capclr_hdr, Cap_reverse);
strcpy(q->capclr_rownorm, Cap_norm);
2002-05-30 09:14:46 +05:30
}
// composite(s), so we do 'em outside and after the if
snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
, q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
2002-06-19 05:15:30 +05:30
q->len_rownorm = strlen(q->capclr_rownorm);
q->len_rowhigh = strlen(q->capclr_rowhigh);
2002-05-30 09:14:46 +05:30
#undef tIF
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
2004-07-07 22:57:26 +05:30
// Show an error, but not right now.
// Due to the postponed opening of ksym, using open_psdb_message,
// if P_WCH had been selected and the program is restarted, the
// message would otherwise be displayed prematurely.
2002-05-30 09:14:46 +05:30
static void msg_save (const char *fmts, ...)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
char tmp[SMLBUFSIZ];
va_list va;
va_start(va, fmts);
2002-09-13 17:12:44 +05:30
vsnprintf(tmp, sizeof(tmp), fmts, va);
2002-05-30 09:14:46 +05:30
va_end(va);
/* we'll add some extra attention grabbers to whatever this is */
2002-12-09 04:21:09 +05:30
snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp));
2002-05-30 09:14:46 +05:30
Msg_awaiting = 1;
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* Show an error message (caller may include a '\a' for sound) */
static void show_msg (const char *str)
2002-02-02 04:17:29 +05:30
{
2004-07-07 00:43:38 +05:30
PUTT("%s%s %s %s%s",
tg2(0, Msg_row),
Curwin->capclr_msg,
str,
Caps_off,
Cap_clr_eol
);
2002-05-30 09:14:46 +05:30
fflush(stdout);
sleep(MSG_SLEEP);
Msg_awaiting = 0;
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* Show an input prompt + larger cursor */
static void show_pmt (const char *str)
{
2004-07-07 00:43:38 +05:30
PUTT("%s%s%s: %s%s",
tg2(0, Msg_row),
Curwin->capclr_pmt,
str,
Cap_curs_huge,
Caps_off
);
2002-05-30 09:14:46 +05:30
fflush(stdout);
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* Show lines with specially formatted elements, but only output
* what will fit within the current screen width.
* Our special formatting consists of:
* "some text <_delimiter_> some more text <_delimiter_>...\n"
* Where <_delimiter_> is a single byte in the range of:
2002-06-19 05:15:30 +05:30
* \01 through \10 (in decimalizee, 1 - 8)
2002-05-30 09:14:46 +05:30
* and is used to select an 'attribute' from a capabilities table
* which is then applied to the *preceding* substring.
* Once recognized, the delimiter is replaced with a null character
* and viola, we've got a substring ready to output! Strings or
* substrings without delimiters will receive the Cap_norm attribute.
*
* Caution:
* This routine treats all non-delimiter bytes as displayable
* data subject to our screen width marching orders. If callers
* embed non-display data like tabs or terminfo strings in our
* glob, a line will truncate incorrectly at best. Worse case
* would be truncation of an embedded tty escape sequence.
*
* Tabs must always be avoided or our efforts are wasted and
* lines will wrap. To lessen but not eliminate the risk of
* terminfo string truncation, such non-display stuff should
2002-06-19 05:15:30 +05:30
* be placed at the beginning of a "short" line.
* (and as for tabs, gimme 1 more color then no worries, mate) */
static void show_special (int interact, const char *glob)
2002-06-19 05:15:30 +05:30
{ /* note: the following is for documentation only,
the real captab is now found in a group's WIN_t !
+------------------------------------------------------+
| char *captab[] = { : Cap's/Delim's |
| Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 |
| Sum_color, = \03 |
| Msg_color, Pmt_color, = \04, \05 |
| Hdr_color, = \06 |
| Row_color_high, = \07 |
| Row_color_norm }; = \10 [octal!] |
+------------------------------------------------------+ */
2004-07-07 00:43:38 +05:30
char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ];
char *rp, *cap, *lin_end, *sub_beg, *sub_end;
2002-05-30 09:14:46 +05:30
int room;
/* handle multiple lines passed in a bunch */
while ((lin_end = strchr(glob, '\n'))) {
/* create a local copy we can extend and otherwise abuse */
memcpy(lin, glob, (unsigned)(lin_end - glob));
2002-05-30 09:14:46 +05:30
/* zero terminate this part and prepare to parse substrings */
lin[lin_end - glob] = '\0';
2002-05-30 09:14:46 +05:30
room = Screen_cols;
sub_beg = sub_end = lin;
*(rp = row) = '\0';
2002-05-30 09:14:46 +05:30
while (*sub_beg) {
switch (*sub_end) {
2002-06-19 05:15:30 +05:30
case 0: /* no end delim, captab makes normal */
2002-05-30 09:14:46 +05:30
*(sub_end + 1) = '\0'; /* extend str end, then fall through */
case 1 ... 8:
2002-06-19 05:15:30 +05:30
cap = Curwin->captab[(int)*sub_end];
2002-05-30 09:14:46 +05:30
*sub_end = '\0';
snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
rp = scat(rp, tmp);
2002-05-30 09:14:46 +05:30
room -= (sub_end - sub_beg);
sub_beg = ++sub_end;
break;
2002-06-19 05:15:30 +05:30
default: /* nothin' special, just text */
2002-05-30 09:14:46 +05:30
++sub_end;
}
2002-12-09 04:21:09 +05:30
if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
}
2002-05-30 09:14:46 +05:30
if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
else PUFF("%s%s\n", row, Cap_clr_eol);
2002-05-30 09:14:46 +05:30
glob = ++lin_end; /* point to next line (maybe) */
} /* end: while 'lines' */
/* If there's anything left in the glob (by virtue of no trailing '\n'),
2002-05-30 09:14:46 +05:30
it probably means caller wants to retain cursor position on this final
line. That, in turn, means we're interactive and so we'll just do our
'fit-to-screen' thingy... */
if (*glob) PUTT("%.*s", Screen_cols, glob);
2002-02-02 04:17:29 +05:30
}
2004-07-15 06:47:15 +05:30
2002-06-19 05:15:30 +05:30
/*###### Small Utility routines ########################################*/
2002-05-30 09:14:46 +05:30
2004-07-15 06:47:15 +05:30
// Get a string from the user
2002-06-19 05:15:30 +05:30
static char *ask4str (const char *prompt)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
static char buf[GETBUFSIZ];
show_pmt(prompt);
memset(buf, '\0', sizeof(buf));
chin(1, buf, sizeof(buf) - 1);
putp(Cap_curs_norm);
2002-12-09 04:21:09 +05:30
return strim_0(buf);
2002-05-30 09:14:46 +05:30
}
2002-02-02 04:17:29 +05:30
2004-07-15 06:47:15 +05:30
// Get a float from the user
2002-05-30 09:14:46 +05:30
static float get_float (const char *prompt)
{
char *line;
float f;
2002-06-19 05:15:30 +05:30
if (!(*(line = ask4str(prompt)))) return -1;
2002-11-08 06:01:28 +05:30
// note: we're not allowing negative floats
2002-06-19 05:15:30 +05:30
if (strcspn(line, ",.1234567890")) {
2002-05-30 09:14:46 +05:30
show_msg("\aNot valid");
return -1;
}
sscanf(line, "%f", &f);
return f;
2002-02-02 04:17:29 +05:30
}
2004-07-15 06:47:15 +05:30
// Get an integer from the user
2002-05-30 09:14:46 +05:30
static int get_int (const char *prompt)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
char *line;
int n;
2002-06-19 05:15:30 +05:30
if (!(*(line = ask4str(prompt)))) return -1;
2002-11-08 06:01:28 +05:30
// note: we've got to allow negative ints (renice)
2002-05-30 09:14:46 +05:30
if (strcspn(line, "-1234567890")) {
show_msg("\aNot valid");
return -1;
}
sscanf(line, "%d", &n);
return n;
}
2002-02-02 04:17:29 +05:30
2002-05-30 09:14:46 +05:30
/*
* Do some scaling stuff.
* We'll interpret 'num' as one of the following types and
* try to format it to fit 'width'.
* SK_no (0) it's a byte count
* SK_Kb (1) it's kilobytes
* SK_Mb (2) it's megabytes
* SK_Gb (3) it's gigabytes */
2002-10-12 09:55:57 +05:30
static const char *scale_num (unsigned num, const int width, const unsigned type)
2002-05-30 09:14:46 +05:30
{
2002-10-07 01:42:08 +05:30
/* kilobytes, megabytes, gigabytes, duh! */
static float scale[] = { 1024, 1024*1024, 1024*1024*1024, 0 };
2002-05-30 09:14:46 +05:30
/* kilo, mega, giga, none */
2002-06-19 05:15:30 +05:30
#ifdef CASEUP_SCALE
2002-05-30 09:14:46 +05:30
static char nextup[] = { 'K', 'M', 'G', 0 };
#else
static char nextup[] = { 'k', 'm', 'g', 0 };
#endif
static char buf[TNYBUFSIZ];
2002-10-07 01:42:08 +05:30
float *dp;
2002-05-30 09:14:46 +05:30
char *up;
/* try an unscaled version first... */
if (width >= snprintf(buf, sizeof(buf), "%u", num)) return buf;
2002-05-30 09:14:46 +05:30
/* now try successively higher types until it fits */
for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
/* the most accurate version */
if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up))
return buf;
2002-05-30 09:14:46 +05:30
/* the integer version */
if (width >= snprintf(buf, sizeof(buf), "%d%c", (int)(num / *dp), *up))
return buf;
2002-05-30 09:14:46 +05:30
}
/* well shoot, this outta' fit... */
2002-10-02 05:40:30 +05:30
return "?";
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* Do some scaling stuff.
2002-11-08 06:01:28 +05:30
* format 'tics' to fit 'width'. */
2002-12-05 04:18:30 +05:30
static const char *scale_tics (TIC_t tics, const int width)
2002-02-02 04:17:29 +05:30
{
2002-06-19 05:15:30 +05:30
#ifdef CASEUP_SCALE
2002-10-07 01:42:08 +05:30
#define HH "%uH"
#define DD "%uD"
#define WW "%uW"
2002-05-30 09:14:46 +05:30
#else
2002-10-07 01:42:08 +05:30
#define HH "%uh"
#define DD "%ud"
#define WW "%uw"
2002-05-30 09:14:46 +05:30
#endif
static char buf[TNYBUFSIZ];
2002-11-08 06:01:28 +05:30
unsigned long nt; // narrow time, for speed on 32-bit
unsigned cc; // centiseconds
unsigned nn; // multi-purpose whatever
2002-11-09 07:30:52 +05:30
nt = (tics * 100ull) / Hertz;
2002-11-08 06:01:28 +05:30
cc = nt % 100; // centiseconds past second
nt /= 100; // total seconds
nn = nt % 60; // seconds past the minute
nt /= 60; // total minutes
if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
return buf;
2002-11-08 06:01:28 +05:30
if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
return buf;
nn = nt % 60; // minutes past the hour
nt /= 60; // total hours
2002-11-08 06:01:28 +05:30
if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
return buf;
2002-11-08 06:01:28 +05:30
nn = nt; // now also hours
if (width >= snprintf(buf, sizeof buf, HH, nn))
return buf;
2002-11-08 06:01:28 +05:30
nn /= 24; // now days
if (width >= snprintf(buf, sizeof buf, DD, nn))
return buf;
nn /= 7; // now weeks
if (width >= snprintf(buf, sizeof buf, WW, nn))
return buf;
// well shoot, this outta' fit...
2002-10-02 05:40:30 +05:30
return "?";
2002-10-07 01:42:08 +05:30
#undef HH
#undef DD
#undef WW
2002-02-02 04:17:29 +05:30
}
2002-12-10 08:31:17 +05:30
#include <pwd.h>
static int selection_type;
static uid_t selection_uid;
// FIXME: this is "temporary" code we hope
static int good_uid(const proc_t *restrict const pp){
switch(selection_type){
case 'p':
return 1;
case 0:
return 1;
case 'U':
if (pp->ruid == selection_uid) return 1;
if (pp->suid == selection_uid) return 1;
if (pp->fuid == selection_uid) return 1;
// FALLTHROUGH
case 'u':
if (pp->euid == selection_uid) return 1;
// FALLTHROUGH
default:
; // don't know what it is; find bugs fast
}
return 0;
}
// swiped from ps, and ought to be in libproc
static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){
struct passwd *passwd_data;
char *endp;
unsigned long num;
static const char uidrange[] = "User ID out of range.";
static const char uidexist[] = "User name does not exist.";
num = strtoul(str, &endp, 0);
if(*endp != '\0'){ /* hmmm, try as login name */
passwd_data = getpwnam(str);
if(!passwd_data) return uidexist;
num = passwd_data->pw_uid;
}
if(num > 0xfffffffeUL) return uidrange;
*ret = num;
return 0;
}
2002-06-19 05:15:30 +05:30
/*###### Library Alternatives ##########################################*/
2002-02-02 04:17:29 +05:30
2002-05-30 09:14:46 +05:30
/*
2002-06-19 05:15:30 +05:30
* Handle our own memory stuff without the risk of leaving the
* user's terminal in an ugly state should things go sour. */
2002-05-30 09:14:46 +05:30
2002-11-27 13:51:30 +05:30
static void *alloc_c (unsigned numb) MALLOC;
static void *alloc_c (unsigned numb)
2002-06-19 05:15:30 +05:30
{
void * p;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
if (!numb) ++numb;
if (!(p = calloc(1, numb)))
2002-10-02 05:40:30 +05:30
std_err("failed memory allocate");
2002-06-19 05:15:30 +05:30
return p;
2002-02-02 04:17:29 +05:30
}
2002-11-27 13:51:30 +05:30
static void *alloc_r (void *q, unsigned numb) MALLOC;
static void *alloc_r (void *q, unsigned numb)
2002-02-02 04:17:29 +05:30
{
2002-06-19 05:15:30 +05:30
void *p;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
if (!numb) ++numb;
if (!(p = realloc(q, numb)))
2002-10-02 05:40:30 +05:30
std_err("failed memory allocate");
2002-06-19 05:15:30 +05:30
return p;
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* This guy's modeled on libproc's 'five_cpu_numbers' function except
2002-12-05 04:18:30 +05:30
* we preserve all cpu data in our CPU_t array which is organized
2002-10-02 05:40:30 +05:30
* as follows:
* cpus[0] thru cpus[n] == tics for each separate cpu
* cpus[Cpu_tot] == tics from the 1st /proc/stat line */
2002-12-05 04:18:30 +05:30
static CPU_t *cpus_refresh (CPU_t *cpus)
2002-10-02 05:40:30 +05:30
{
static FILE *fp = NULL;
int i;
2004-07-07 00:43:38 +05:30
int num;
2002-11-08 06:01:28 +05:30
// enough for a /proc/stat CPU line (not the intr line)
2002-10-07 01:42:08 +05:30
char buf[SMLBUFSIZ];
2002-10-02 05:40:30 +05:30
2002-11-08 06:01:28 +05:30
/* by opening this file once, we'll avoid the hit on minor page faults
(sorry Linux, but you'll have to close it for us) */
2002-10-02 05:40:30 +05:30
if (!fp) {
if (!(fp = fopen("/proc/stat", "r")))
std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
2002-12-05 04:18:30 +05:30
/* note: we allocate one more CPU_t than Cpu_tot so that the last slot
2002-10-02 05:40:30 +05:30
can hold tics representing the /proc/stat cpu summary (the first
line read) -- that slot supports our View_CPUSUM toggle */
2002-12-05 04:18:30 +05:30
cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
2002-10-02 05:40:30 +05:30
}
rewind(fp);
fflush(fp);
2002-11-08 06:01:28 +05:30
// first value the last slot with the cpu summary line
2002-10-07 01:42:08 +05:30
if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
2003-09-08 07:09:49 +05:30
cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
2004-07-07 05:41:19 +05:30
num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
2004-07-07 00:43:38 +05:30
&cpus[Cpu_tot].u,
&cpus[Cpu_tot].n,
&cpus[Cpu_tot].s,
&cpus[Cpu_tot].i,
&cpus[Cpu_tot].w,
&cpus[Cpu_tot].x,
&cpus[Cpu_tot].y
);
if (num < 4)
2002-10-02 05:40:30 +05:30
std_err("failed /proc/stat read");
2004-07-07 05:41:19 +05:30
2002-11-08 06:01:28 +05:30
// and just in case we're 2.2.xx compiled without SMP support...
2004-07-07 05:41:19 +05:30
if (Cpu_tot == 1) {
cpus[1].id = 0;
memcpy(cpus, &cpus[1], sizeof(CPU_t));
}
2002-10-02 05:40:30 +05:30
2002-11-08 06:01:28 +05:30
// now value each separate cpu's tics
for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
2002-10-07 01:42:08 +05:30
if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
2003-09-08 07:09:49 +05:30
cpus[i].x = 0; // FIXME: can't tell by kernel version number
cpus[i].y = 0; // FIXME: can't tell by kernel version number
2004-07-07 05:41:19 +05:30
num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
&cpus[i].id,
2004-07-07 00:43:38 +05:30
&cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y
);
if (num < 4)
2002-10-02 05:40:30 +05:30
std_err("failed /proc/stat read");
}
return cpus;
}
2002-11-08 06:01:28 +05:30
/*
* Refresh procs *Helper* function to eliminate yet one more need
* to loop through our darn proc_t table. He's responsible for:
* 1) calculating the elapsed time since the previous frame
* 2) counting the number of tasks in each state (run, sleep, etc)
2002-12-05 04:18:30 +05:30
* 3) maintaining the HST_t's and priming the proc_t pcpu field
2002-11-08 06:01:28 +05:30
* 4) establishing the total number tasks for this frame */
2002-11-25 15:46:33 +05:30
static void prochlp (proc_t *this)
2002-11-08 06:01:28 +05:30
{
2002-12-05 04:18:30 +05:30
static HST_t *hist_sav = NULL;
static HST_t *hist_new = NULL;
2002-11-08 06:01:28 +05:30
static unsigned hist_siz = 0; // number of structs
static unsigned maxt_sav; // prior frame's max tasks
2002-12-05 04:18:30 +05:30
TIC_t tics;
2002-11-08 06:01:28 +05:30
2002-12-09 04:21:09 +05:30
if (unlikely(!this)) {
2002-11-08 06:01:28 +05:30
static struct timeval oldtimev;
struct timeval timev;
struct timezone timez;
2002-12-05 04:18:30 +05:30
HST_t *hist_tmp;
2002-11-08 06:01:28 +05:30
float et;
gettimeofday(&timev, &timez);
et = (timev.tv_sec - oldtimev.tv_sec)
+ (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
oldtimev.tv_sec = timev.tv_sec;
oldtimev.tv_usec = timev.tv_usec;
// if in Solaris mode, adjust our scaling for all cpus
2002-12-05 04:18:30 +05:30
Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
2002-11-08 06:01:28 +05:30
maxt_sav = Frame_maxtask;
Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
// reuse memory each time around
hist_tmp = hist_sav;
hist_sav = hist_new;
hist_new = hist_tmp;
2002-12-05 04:18:30 +05:30
// prep for our binary search by sorting the last frame's HST_t's
qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
2002-11-08 06:01:28 +05:30
return;
}
switch (this->state) {
case 'R':
Frame_running++;
break;
case 'S':
case 'D':
Frame_sleepin++;
break;
case 'T':
Frame_stopped++;
break;
case 'Z':
Frame_zombied++;
break;
}
2002-12-09 04:21:09 +05:30
if (unlikely(Frame_maxtask+1 >= hist_siz)) {
2002-11-08 06:01:28 +05:30
hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25%
2002-12-05 04:18:30 +05:30
hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz);
hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz);
2002-11-08 06:01:28 +05:30
}
/* calculate time in this process; the sum of user time (utime) and
system time (stime) -- but PLEASE dont waste time and effort on
calcs and saves that go unused, like the old top! */
2003-09-20 13:59:55 +05:30
hist_new[Frame_maxtask].pid = this->tid;
2002-11-08 06:01:28 +05:30
hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
2002-12-15 03:00:58 +05:30
#if 0
2002-11-25 15:46:33 +05:30
{ int i;
int lo = 0;
int hi = maxt_sav - 1;
2002-11-08 06:01:28 +05:30
// find matching entry from previous frame and make ticks elapsed
while (lo <= hi) {
i = (lo + hi) / 2;
2003-09-20 13:59:55 +05:30
if (this->tid < hist_sav[i].pid)
2002-11-08 06:01:28 +05:30
hi = i - 1;
2003-09-20 13:59:55 +05:30
else if (likely(this->tid > hist_sav[i].pid))
2002-11-08 06:01:28 +05:30
lo = i + 1;
else {
tics -= hist_sav[i].tics;
break;
}
}
}
2002-12-15 03:00:58 +05:30
#else
{
HST_t tmp;
const HST_t *ptr;
2003-09-20 13:59:55 +05:30
tmp.pid = this->tid;
2002-12-15 03:00:58 +05:30
ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
if(ptr) tics -= ptr->tics;
}
#endif
2002-12-05 04:18:30 +05:30
// we're just saving elapsed tics, to be converted into %cpu if
// this task wins it's displayable screen row lottery... */
2002-11-08 06:01:28 +05:30
this->pcpu = tics;
2002-12-08 04:06:21 +05:30
// if (Frames_maxcmdln) { }
2002-11-08 06:01:28 +05:30
// shout this to the world with the final call (or us the next time in)
Frame_maxtask++;
}
2002-10-02 05:40:30 +05:30
/*
* This guy's modeled on libproc's 'readproctab' function except
2002-06-19 05:15:30 +05:30
* we reuse and extend any prior proc_t's. He's been customized
* for our specific needs and to avoid the use of <stdarg.h> */
2002-11-08 06:01:28 +05:30
static proc_t **procs_refresh (proc_t **table, int flags)
2002-02-02 04:17:29 +05:30
{
2002-11-08 06:01:28 +05:30
#define PTRsz sizeof(proc_t *)
2002-06-19 05:15:30 +05:30
#define ENTsz sizeof(proc_t)
2002-11-08 06:01:28 +05:30
static unsigned savmax = 0; // first time, Bypass: (i)
proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii)
2002-11-27 05:54:01 +05:30
unsigned curmax = 0; // every time (jeeze)
2002-06-19 05:15:30 +05:30
PROCTAB* PT;
2005-06-22 00:50:39 +05:30
static int show_threads_was_enabled = 0; // optimization
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
prochlp(NULL); // prep for a new frame
if (Monpidsidx)
2003-02-11 13:49:12 +05:30
PT = openproc(flags, Monpids);
2002-11-08 06:01:28 +05:30
else
2002-06-19 05:15:30 +05:30
PT = openproc(flags);
2002-11-08 06:01:28 +05:30
// i) Allocated Chunks: *Existing* table; refresh + reuse
2005-06-22 00:50:39 +05:30
if (!(CHKw(Curwin, Show_THREADS))) {
while (curmax < savmax) {
if (table[curmax]->cmdline) {
unsigned idx;
// Skip if Show_THREADS was never enabled
if (show_threads_was_enabled) {
for (idx = curmax + 1; idx < savmax; idx++) {
if (table[idx]->cmdline == table[curmax]->cmdline)
table[idx]->cmdline = NULL;
}
}
free(*table[curmax]->cmdline);
table[curmax]->cmdline = NULL;
}
if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
prochlp(ptsk); // tally & complete this proc_t
++curmax;
}
}
else { // show each thread in a process separately
while (curmax < savmax) {
proc_t *ttsk;
if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
show_threads_was_enabled = 1;
while (curmax < savmax) {
unsigned idx;
if (table[curmax]->cmdline) {
// threads share the same cmdline storage. 'table' is
// qsort()ed, so must look through the rest of the table.
for (idx = curmax + 1; idx < savmax; idx++) {
if (table[idx]->cmdline == table[curmax]->cmdline)
table[idx]->cmdline = NULL;
}
free(*table[curmax]->cmdline); // only free once
table[curmax]->cmdline = NULL;
}
if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
prochlp(ttsk);
++curmax;
}
free(ptsk); // readproc() proc_t not used
2002-11-08 09:04:20 +05:30
}
2002-06-19 05:15:30 +05:30
}
2002-11-08 06:01:28 +05:30
// ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
2005-06-22 00:50:39 +05:30
if (!(CHKw(Curwin, Show_THREADS))) {
while (ptsk) {
// realloc as we go, keeping 'table' ahead of 'currmax++'
table = alloc_r(table, (curmax + 1) * PTRsz);
// here, readproc will allocate the underlying proc_t stg
if (likely(ptsk = readproc(PT, NULL))) {
prochlp(ptsk); // tally & complete this proc_t
table[curmax++] = ptsk;
}
}
}
else { // show each thread in a process separately
while (ptsk) {
proc_t *ttsk;
if (likely(ptsk = readproc(PT, NULL))) {
show_threads_was_enabled = 1;
while (1) {
table = alloc_r(table, (curmax + 1) * PTRsz);
if (!(ttsk = readtask(PT, ptsk, NULL))) break;
prochlp(ttsk);
table[curmax++] = ttsk;
}
free(ptsk); // readproc() proc_t not used
}
2002-11-08 06:01:28 +05:30
}
2002-06-19 05:15:30 +05:30
}
closeproc(PT);
2002-11-08 06:01:28 +05:30
// iii) Chunkless: make 'eot' entry, after ensuring proc_t exists
2002-06-19 05:15:30 +05:30
if (curmax >= savmax) {
table = alloc_r(table, (curmax + 1) * PTRsz);
2002-11-08 06:01:28 +05:30
// here, we must allocate the underlying proc_t stg ourselves
table[curmax] = alloc_c(ENTsz);
2002-06-19 05:15:30 +05:30
savmax = curmax + 1;
}
2002-11-08 06:01:28 +05:30
// this frame's end, but not necessarily end of allocated space
2003-09-20 13:59:55 +05:30
table[curmax]->tid = -1;
return table;
2002-06-19 05:15:30 +05:30
#undef PTRsz
#undef ENTsz
2002-02-02 04:17:29 +05:30
}
2002-12-05 04:18:30 +05:30
/*###### Field Table/RCfile compatability support ######################*/
// from either 'stat' or 'status' (preferred), via bits not otherwise used
#define L_EITHER PROC_SPARE_1
// These are the Fieldstab.lflg values used here and in reframewins.
// (own identifiers as documentation and protection against changes)
2002-11-30 21:26:53 +05:30
#define L_stat PROC_FILLSTAT
#define L_statm PROC_FILLMEM
#define L_status PROC_FILLSTATUS
#define L_CMDLINE L_EITHER | PROC_FILLARG
2002-12-05 07:17:36 +05:30
#define L_EUSER PROC_FILLUSR
#define L_RUSER L_status | PROC_FILLUSR
2002-11-30 21:26:53 +05:30
#define L_GROUP L_status | PROC_FILLGRP
#define L_NONE 0
// for reframewins and summary_show 1st pass
2002-11-30 21:26:53 +05:30
#define L_DEFAULT PROC_FILLSTAT
// a temporary macro, soon to be undef'd...
2002-12-05 04:18:30 +05:30
#define SF(f) (QFP_t)sort_P_ ## f
2002-11-30 21:26:53 +05:30
/* These are our gosh darn 'Fields' !
They MUST be kept in sync with pflags !!
note: for integer data, the length modifiers found in .fmts may
NOT reflect the true field type found in proc_t -- this plus
a cast when/if displayed provides minimal width protection. */
2002-12-05 04:18:30 +05:30
static FLD_t Fieldstab[] = {
2002-11-30 21:26:53 +05:30
/* .lflg anomolies:
P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid)
P_CPU, L_stat - never filled by libproc, but requires times (pcpu)
P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline)
L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit !
keys head fmts width scale sort desc lflg
------ ----------- ------- ------ ----- ----- ---------------------- -------- */
2004-07-15 06:47:15 +05:30
{ "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE },
{ "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER },
{ "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER },
{ "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE },
{ "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER },
{ "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP },
{ "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat },
{ "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat },
{ "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat },
{ "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
{ "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat },
{ "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat },
{ "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat },
{ "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm },
{ "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm },
{ "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm },
{ "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm },
{ "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm },
{ "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm },
{ "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm },
{ "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat },
{ "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm },
{ "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER },
2002-11-30 21:26:53 +05:30
// next entry's special: '.head' will be formatted using table entry's own
// '.fmts' plus runtime supplied conversion args!
2004-07-15 06:47:15 +05:30
{ "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER },
{ "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat },
2002-11-30 21:26:53 +05:30
// next entry's special: the 0's will be replaced with '.'!
2004-07-15 06:47:15 +05:30
{ "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags <sched.h>", L_stat },
2002-12-04 07:32:37 +05:30
#if 0
2004-07-15 06:47:15 +05:30
{ "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat },
{ "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat },
{ "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat },
{ "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
{ "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 },
{ "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
{ "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
2002-12-04 07:32:37 +05:30
#endif
2002-11-30 21:26:53 +05:30
};
#undef SF
2002-12-05 04:18:30 +05:30
/* All right, those-that-follow -- Listen Up!
* For the above table keys and the following present/future rc file
* compatibility support, you have Mr. Albert D. Cahalan to thank.
* He must have been in a 'Christmas spirit'. Were it left to me,
* this top would never have gotten that close to the former top's
* crufty rcfile. Not only is it illogical, it's odoriferous !
*/
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
// used as 'to' and/or 'from' args in the ft_xxx utilities...
#define FT_NEW_fmt 0
#define FT_OLD_fmt 2
2002-11-30 21:26:53 +05:30
2002-12-09 02:03:07 +05:30
#if 0
2002-12-05 04:18:30 +05:30
// convert, or 0 for failure
static int ft_cvt_char (const int fr, const int to, int c) {
2002-11-30 21:26:53 +05:30
int j = -1;
2002-12-05 04:18:30 +05:30
2002-11-30 21:26:53 +05:30
while (++j < MAXTBL(Fieldstab)) {
2002-12-05 04:18:30 +05:30
if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to];
if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1];
2002-11-30 21:26:53 +05:30
}
2002-12-05 04:18:30 +05:30
return 0;
2002-11-30 21:26:53 +05:30
}
2002-12-09 02:03:07 +05:30
#endif
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
// convert
static inline int ft_get_char (const int fr, int i) {
int c;
if (i < 0) return 0;
if (i >= MAXTBL(Fieldstab)) return 0;
c = Fieldstab[i].keys[fr];
2002-12-06 12:23:29 +05:30
if (c == '.') c = 0; // '.' marks a bad entry
2002-12-05 04:18:30 +05:30
return c;
}
2002-12-06 12:23:29 +05:30
#if 0
2002-12-05 04:18:30 +05:30
// convert, or -1 for failure
static int ft_get_idx (const int fr, int c) {
2002-11-30 21:26:53 +05:30
int j = -1;
2002-12-05 04:18:30 +05:30
2002-11-30 21:26:53 +05:30
while (++j < MAXTBL(Fieldstab)) {
2002-12-05 04:18:30 +05:30
if (c == Fieldstab[j].keys[fr]) return j;
if (c == Fieldstab[j].keys[fr+1]) return j;
2002-11-30 21:26:53 +05:30
}
return -1;
}
2002-12-06 12:23:29 +05:30
#endif
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
// convert, or NULL for failure
static const FLD_t *ft_get_ptr (const int fr, int c) {
2002-11-30 21:26:53 +05:30
int j = -1;
2002-12-05 04:18:30 +05:30
2002-11-30 21:26:53 +05:30
while (++j < MAXTBL(Fieldstab)) {
2002-12-05 04:18:30 +05:30
if (c == Fieldstab[j].keys[fr]) return Fieldstab+j;
if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j;
2002-11-30 21:26:53 +05:30
}
return NULL;
}
2002-12-05 04:18:30 +05:30
2002-12-06 12:23:29 +05:30
#if 0
2002-12-05 04:18:30 +05:30
// convert, or NULL for failure
static const FLD_t *ft_idx_to_ptr (const int i) {
if (i < 0) return NULL;
if (i >= MAXTBL(Fieldstab)) return NULL;
return Fieldstab + i;
2002-11-30 21:26:53 +05:30
}
2002-12-05 04:18:30 +05:30
// convert, or -1 for failure
static int ft_ptr_to_idx (const FLD_t *p) {
int i;
if (p < Fieldstab) return -1;
i = p - Fieldstab;
if (i >= MAXTBL(Fieldstab)) return -1;
return i;
2002-11-30 21:26:53 +05:30
}
2002-12-08 04:06:21 +05:30
#endif
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
2002-12-06 12:23:29 +05:30
#if 0
2002-12-05 04:18:30 +05:30
static void rc_bugless (const RCF_t *const rc) {
const RCW_t *w;
int i = 0;
2004-07-07 00:43:38 +05:30
fprintf(stderr,"\n%d %d %f %d\n",
rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
);
2002-12-05 04:18:30 +05:30
while(i < 4) {
w = &rc->win[i++];
2004-07-07 00:43:38 +05:30
fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n",
w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks,
w->summclr, w->msgsclr, w->headclr, w->taskclr
);
2002-11-30 21:26:53 +05:30
}
}
2002-12-05 04:18:30 +05:30
#endif
2002-11-30 21:26:53 +05:30
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window.
// line 1: an eyecatcher, with a shameless advertisement
// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin.
// For each of the 4 windows:
// line a: contains winname, fieldscur
// line b: contains winflags, sortindx, maxtasks
// line c: contains summclr, msgsclr, headclr, taskclr
// line d: if present, would crash procps-3.1.1
2002-12-05 04:18:30 +05:30
static int rc_read_new (const char *const buf, RCF_t *rc) {
2002-06-19 05:15:30 +05:30
int i;
2002-12-05 04:18:30 +05:30
int cnt;
const char *cp;
2002-06-19 05:15:30 +05:30
2002-12-05 04:18:30 +05:30
cp = strstr(buf, "\n\n" RCF_EYECATCHER);
if (!cp) return -1;
cp = strchr(cp + 2, '\n');
if (!cp++) return -2;
2002-06-19 05:15:30 +05:30
2004-07-07 00:43:38 +05:30
cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n",
&rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index
);
2002-12-05 04:18:30 +05:30
if (cnt != 4) return -3;
cp = strchr(cp, '\n');
if (!cp++) return -4;
2002-06-19 05:15:30 +05:30
2002-12-05 04:18:30 +05:30
for (i = 0; i < GROUPSMAX; i++) {
RCW_t *ptr = &rc->win[i];
2002-12-07 06:09:05 +05:30
cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur);
2002-12-05 04:18:30 +05:30
if (cnt != 2) return 5+100*i; // OK to have less than 4 windows
if (WINNAMSIZ <= strlen(ptr->winname)) return -6;
if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7;
cp = strchr(cp, '\n');
if (!cp++) return -(8+100*i);
2002-02-02 04:17:29 +05:30
2004-07-07 00:43:38 +05:30
cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
&ptr->winflags, &ptr->sortindx, &ptr->maxtasks
);
2002-12-05 04:18:30 +05:30
if (cnt != 3) return -(9+100*i);
cp = strchr(cp, '\n');
if (!cp++) return -(10+100*i);
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
&ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
);
2002-12-05 04:18:30 +05:30
if (cnt != 4) return -(11+100*i);
cp = strchr(cp, '\n');
if (!cp++) return -(12+100*i);
2002-12-08 04:06:21 +05:30
while (*cp == '\t') { // skip unknown per-window settings
cp = strchr(cp, '\n');
if (!cp++) return -(13+100*i);
}
2002-11-30 21:26:53 +05:30
}
2002-12-05 04:18:30 +05:30
return 13;
2002-11-30 21:26:53 +05:30
}
2002-12-08 04:06:21 +05:30
2002-12-05 04:18:30 +05:30
static int rc_read_old (const char *const buf, RCF_t *rc) {
2002-12-08 04:06:21 +05:30
const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......";
const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr";
2002-12-04 07:32:37 +05:30
unsigned u;
2002-11-30 21:26:53 +05:30
const char *cp;
unsigned c_show = 0;
2002-12-06 12:23:29 +05:30
int badchar = 0; // allow a limited number of duplicates and junk
2002-11-30 21:26:53 +05:30
char scoreboard[256];
2002-12-04 07:32:37 +05:30
memset(scoreboard, '\0', sizeof scoreboard);
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
cp = buf+2; // skip the "\n\n" we stuck at the beginning
2002-12-04 07:32:37 +05:30
u = 0;
2002-12-05 04:18:30 +05:30
for (;;) {
2002-12-08 04:06:21 +05:30
const char *tmp;
2002-12-04 07:32:37 +05:30
int c = *cp++;
2002-12-09 02:03:07 +05:30
if (u+1 >= sizeof rc->win[0].fieldscur) return -1;
2002-12-05 04:18:30 +05:30
if (c == '\0') return -2;
if (c == '\n') break;
2002-12-04 07:32:37 +05:30
if (c & ~0x7f) return -3;
2002-12-06 12:23:29 +05:30
if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden
if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed
2002-12-04 07:32:37 +05:30
scoreboard[c|0xe0u]++;
2002-12-08 04:06:21 +05:30
tmp = strchr(old,c);
2002-12-08 09:42:33 +05:30
if (!tmp) continue;
c = *((tmp-old)+std);
if (c == '.') continue;
2002-12-06 12:23:29 +05:30
if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed
2002-12-04 07:32:37 +05:30
scoreboard[c&0x1fu]++;
rc->win[0].fieldscur[u++] = c;
}
rc->win[0].fieldscur[u++] = '\0';
2002-12-05 04:18:30 +05:30
if (u < 21) return -6; // catch junk, not good files (had 23 chars in one)
if (u > 33) return -7; // catch junk, not good files (had 29 chars in one)
2002-12-08 04:06:21 +05:30
// fprintf(stderr, "badchar: %d\n", badchar); sleep(2);
if (badchar > 8) return -8; // too much junk
2002-12-05 04:18:30 +05:30
if (!c_show) return -9; // nothing was shown
2002-12-04 07:32:37 +05:30
2002-11-30 21:26:53 +05:30
// rest of file is optional, but better look right if it exists
if (!*cp) return 12;
2002-12-05 04:18:30 +05:30
if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid?
rc->delay_time = *cp - '0';
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
memset(scoreboard, '\0', sizeof(scoreboard));
for (;;) {
int c = *++cp & 0xffu; // protect scoreboard[] from negative char
if (!c) return -14; // not OK to hit EOL w/o '\n'
if (c == '\n') break;
2002-11-30 21:26:53 +05:30
switch (c) {
case ' ':
case '.':
2002-12-05 04:18:30 +05:30
case '0' ... '9':
2002-12-08 04:06:21 +05:30
return -15; // not supposed to have digits here
2002-11-30 21:26:53 +05:30
2002-12-08 04:06:21 +05:30
// case 's': // mostly for global rcfile
2002-12-05 05:58:36 +05:30
// rc->mode_secure = 1;
// break;
2002-11-30 21:26:53 +05:30
case 'S':
rc->win[0].winflags |= Show_CTIMES;
break;
case 'c':
rc->win[0].winflags |= Show_CMDLIN;
break;
case 'i':
rc->win[0].winflags &= ~Show_IDLEPS;
break;
2005-06-22 00:50:39 +05:30
case 'H':
rc->win[0].winflags |= Show_THREADS;
2002-11-30 21:26:53 +05:30
break;
case 'm':
rc->win[0].winflags &= ~View_MEMORY;
break;
case 'l':
rc->win[0].winflags &= ~View_LOADAV;
break;
case 't':
rc->win[0].winflags &= ~View_STATES;
break;
case 'I':
2002-12-05 04:18:30 +05:30
rc->mode_irixps = 0;
2002-11-30 21:26:53 +05:30
break;
case 'M':
c = 0; // for scoreboard
rc->win[0].sortindx = P_MEM;
break;
case 'P':
c = 0; // for scoreboard
rc->win[0].sortindx = P_CPU;
break;
2002-12-08 04:06:21 +05:30
case 'A': // supposed to be start_time
2002-11-30 21:26:53 +05:30
c = 0; // for scoreboard
2002-12-08 04:06:21 +05:30
rc->win[0].sortindx = P_PID;
2002-11-30 21:26:53 +05:30
break;
case 'T':
c = 0; // for scoreboard
rc->win[0].sortindx = P_TM2;
break;
case 'N':
c = 0; // for scoreboard
rc->win[0].sortindx = P_PID;
break;
default:
// just ignore it, except for the scoreboard of course
break;
}
2002-12-08 04:06:21 +05:30
if (scoreboard[c]) return -16; // duplicates not allowed
2002-11-30 21:26:53 +05:30
scoreboard[c] = 1;
}
return 17;
}
2002-12-05 04:18:30 +05:30
static void rc_write_new (FILE *fp) {
int i;
2002-11-30 21:26:53 +05:30
2004-07-07 00:43:38 +05:30
fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
Myname
);
2002-12-05 04:18:30 +05:30
fprintf(fp, RCF_DEPRECATED
2004-07-07 00:43:38 +05:30
"Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n",
Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk)
);
2002-11-30 21:26:53 +05:30
for (i = 0; i < GROUPSMAX; i++) {
2002-12-05 04:18:30 +05:30
char buf[40];
2002-12-05 08:30:42 +05:30
char *cp = Winstk[i].rc.fieldscur;
2002-12-05 04:18:30 +05:30
int j = 0;
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
while (j < 36) {
int c = *cp++ & 0xff;
switch (c) {
case '.':
case 1 ... ' ':
case 0x7f ... 0xff:
continue; // throw away junk (some of it)
default:
buf[j++] = c; // gets the '\0' too
2002-11-30 21:26:53 +05:30
}
2002-12-05 04:18:30 +05:30
if (!c) break;
2002-11-30 21:26:53 +05:30
}
2004-07-07 00:43:38 +05:30
fprintf(fp, "%s\tfieldscur=%s\n",
Winstk[i].rc.winname, buf
);
fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
);
fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n",
Winstk[i].rc.summclr, Winstk[i].rc.msgsclr,
Winstk[i].rc.headclr, Winstk[i].rc.taskclr
);
2002-11-30 21:26:53 +05:30
}
}
2002-12-05 04:18:30 +05:30
static const char *rc_write_whatever (void) {
FILE *fp = fopen(Rc_name, "w");
if (!fp) return strerror(errno);
rc_write_new(fp);
fclose(fp);
return NULL;
2002-11-30 21:26:53 +05:30
}
2002-12-15 03:00:58 +05:30
2002-12-05 04:18:30 +05:30
/*###### Startup routines ##############################################*/
2004-07-07 05:41:19 +05:30
// No mater what *they* say, we handle the really really BIG and
// IMPORTANT stuff upon which all those lessor functions depend!
2002-12-05 04:18:30 +05:30
static void before (char *me)
{
2002-11-30 21:26:53 +05:30
int i;
2002-12-05 04:18:30 +05:30
/* setup our program name -- big! */
Myname = strrchr(me, '/');
if (Myname) ++Myname; else Myname = me;
/* establish cpu particulars -- even bigger! */
Cpu_tot = smp_num_cpus;
if (linux_version_code > LINUX_VERSION(2, 5, 41))
States_fmts = STATES_line2x5;
2003-09-08 07:09:49 +05:30
if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-(
States_fmts = STATES_line2x6;
2002-12-05 04:18:30 +05:30
/* get virtual page size -- nearing huge! */
Page_size = getpagesize();
2003-09-06 10:41:04 +05:30
i = Page_size;
while(i>1024){
i >>= 1;
page_to_kb_shift++;
}
2005-01-06 05:43:12 +05:30
// commented out because it is redundant with the table content
// Fieldstab[P_CPU].head = " %CPU";
// Fieldstab[P_CPU].fmts = " %#4.1f";
2005-01-06 05:43:12 +05:30
pcpu_max_value = 99.9;
if(Rc.mode_irixps && smp_num_cpus>1){
// good for 100 CPUs per process
pcpu_max_value = 9999.0;
Fieldstab[P_CPU].fmts = " %4.0f";
}
2005-01-06 05:43:12 +05:30
Fieldstab[P_CPN].head = " P";
Fieldstab[P_CPN].fmts = " %1u";
if(smp_num_cpus>9){
Fieldstab[P_CPN].head = " P";
Fieldstab[P_CPN].fmts = " %2u";
}
if(smp_num_cpus>99){
Fieldstab[P_CPN].head = " P";
Fieldstab[P_CPN].fmts = " %3u";
}
if(smp_num_cpus>999){
Fieldstab[P_CPN].head = " P";
Fieldstab[P_CPN].fmts = " %4u";
}
2005-01-26 13:25:52 +05:30
{
static char pid_fmt[6];
unsigned pid_digits = get_pid_digits();
if(pid_digits<4) pid_digits=4;
snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits);
Fieldstab[P_PID].fmts = pid_fmt;
Fieldstab[P_PID].head = " PID" + 10 - pid_digits;
Fieldstab[P_PPD].fmts = pid_fmt;
Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits;
}
2002-12-05 04:18:30 +05:30
}
2002-12-05 07:17:36 +05:30
2004-07-07 00:43:38 +05:30
// Config file read *helper* function.
// Anything missing won't show as a choice in the field editor,
// so make sure there is exactly one of each letter.
//
// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x
// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too.
2002-12-06 12:23:29 +05:30
static void confighlp (char *fields) {
unsigned upper[PFLAGSSIZ];
unsigned lower[PFLAGSSIZ];
2002-12-05 07:17:36 +05:30
char c;
char *cp;
2002-12-05 08:47:32 +05:30
2002-12-05 07:17:36 +05:30
memset(upper, '\0', sizeof upper);
memset(lower, '\0', sizeof lower);
cp = fields;
for (;;) {
c = *cp++;
if (!c) break;
if(isupper(c)) upper[c&0x1f]++;
else lower[c&0x1f]++;
}
c = 'a';
while (c <= 'z') {
if (upper[c&0x1f] && lower[c&0x1f]) {
2002-12-06 12:23:29 +05:30
lower[c&0x1f] = 0; // got both, so wipe out unseen column
2002-12-05 07:17:36 +05:30
for (;;) {
cp = strchr(fields, c);
2002-12-06 12:23:29 +05:30
if (cp) memmove(cp, cp+1, strlen(cp));
2002-12-05 07:17:36 +05:30
else break;
}
}
2002-12-08 04:06:21 +05:30
while (lower[c&0x1f] > 1) { // got too many a..z
2002-12-05 07:17:36 +05:30
lower[c&0x1f]--;
cp = strchr(fields, c);
2002-12-06 12:23:29 +05:30
memmove(cp, cp+1, strlen(cp));
2002-12-05 07:17:36 +05:30
}
2002-12-08 04:06:21 +05:30
while (upper[c&0x1f] > 1) { // got too many A..Z
2002-12-05 07:17:36 +05:30
upper[c&0x1f]--;
cp = strchr(fields, toupper(c));
2002-12-06 12:23:29 +05:30
memmove(cp, cp+1, strlen(cp));
2002-12-05 07:17:36 +05:30
}
2002-12-08 04:06:21 +05:30
if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing
2002-12-05 07:17:36 +05:30
lower[c&0x1f]++;
2002-12-06 12:23:29 +05:30
memmove(fields+1, fields, strlen(fields)+1);
2002-12-05 07:17:36 +05:30
fields[0] = c;
}
c++;
}
}
2002-12-06 12:23:29 +05:30
2004-07-07 00:43:38 +05:30
// First attempt to read the /etc/rcfile which contains two lines
// consisting of the secure mode switch and an update interval.
// It's presence limits what ordinary users are allowed to do.
// (it's actually an old-style config file)
//
// Then build the local rcfile name and try to read a crufty old-top
// rcfile (whew, odoriferous), which may contain an embedded new-style
// rcfile. Whether embedded or standalone, new-style rcfile values
// will always override that crufty stuff!
// note: If running in secure mode via the /etc/rcfile,
// Delay_time will be ignored except for root.
2002-12-05 04:18:30 +05:30
static void configs_read (void)
{
2002-12-06 12:23:29 +05:30
const RCF_t def_rcf = DEF_RCFILE;
2002-12-05 04:18:30 +05:30
char fbuf[MEDBUFSIZ];
int i, fd;
RCF_t rcf;
float delay = Rc.delay_time;
2002-12-06 12:23:29 +05:30
// read part of an old-style config in /etc/toprc
2002-12-05 04:18:30 +05:30
fd = open(SYS_RCFILESPEC, O_RDONLY);
if (fd > 0) {
ssize_t num;
num = read(fd, fbuf, sizeof(fbuf) - 1);
if (num > 0) {
2002-12-05 05:58:36 +05:30
const char *sec = strchr(fbuf, 's');
const char *eol = strchr(fbuf, '\n');
if (eol) {
2002-12-05 06:08:57 +05:30
const char *two = eol + 1; // line two
2002-12-05 05:58:36 +05:30
if (sec < eol) Secure_mode = !!sec;
eol = strchr(two, '\n');
if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two);
}
2002-11-30 21:26:53 +05:30
}
2002-12-05 04:18:30 +05:30
close(fd);
2002-11-30 21:26:53 +05:30
}
2002-02-02 04:17:29 +05:30
if (getenv("TOPRC")) { // should switch on Myname before documenting this?
// not the most optimal here...
snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC"));
} else {
snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew...
if (getenv("HOME"))
snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
}
2002-11-30 21:26:53 +05:30
2002-12-06 12:23:29 +05:30
rcf = def_rcf;
2002-12-05 04:18:30 +05:30
fd = open(Rc_name, O_RDONLY);
if (fd > 0) {
ssize_t num;
num = read(fd, fbuf+2, sizeof(fbuf) -3);
if (num > 0) {
fbuf[0] = '\n';
fbuf[1] = '\n';
fbuf[num+2] = '\0';
2002-12-08 04:06:21 +05:30
//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
//sleep(2);
if (rc_read_new(fbuf, &rcf) < 0) {
rcf = def_rcf; // on failure, maybe mangled
if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf;
}
2002-12-05 04:18:30 +05:30
delay = rcf.delay_time;
}
close(fd);
}
2002-11-30 21:26:53 +05:30
2002-12-05 04:18:30 +05:30
// update Rc defaults, establish a Curwin and fix up the window stack
Rc.mode_altscr = rcf.mode_altscr;
Rc.mode_irixps = rcf.mode_irixps;
2002-12-05 05:58:36 +05:30
if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0;
2002-12-05 08:30:42 +05:30
Curwin = &Winstk[rcf.win_index];
2002-12-05 07:17:36 +05:30
for (i = 0; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]);
2002-12-06 12:23:29 +05:30
confighlp(Winstk[i].rc.fieldscur);
2002-12-05 07:17:36 +05:30
}
2002-12-05 04:18:30 +05:30
// lastly, establish the true runtime secure mode and delay time
2002-12-05 06:08:57 +05:30
if (!getuid()) Secure_mode = 0;
if (!Secure_mode) Rc.delay_time = delay;
2002-11-30 21:26:53 +05:30
}
2002-02-02 04:17:29 +05:30
2002-12-05 04:18:30 +05:30
2004-07-07 00:43:38 +05:30
// Parse command line arguments.
// Note: it's assumed that the rc file(s) have already been read
// and our job is to see if any of those options are to be
// overridden -- we'll force some on and negate others in our
// best effort to honor the loser's (oops, user's) wishes...
2002-06-19 05:15:30 +05:30
static void parse_args (char **args)
2002-02-02 04:17:29 +05:30
{
2002-06-19 05:15:30 +05:30
/* differences between us and the former top:
2002-09-13 17:12:44 +05:30
-C (separate CPU states for SMP) is left to an rcfile
2002-12-06 12:23:29 +05:30
-p (pid monitoring) allows a comma delimited list
2002-06-19 05:15:30 +05:30
-q (zero delay) eliminated as redundant, incomplete and inappropriate
use: "nice -n-10 top -d0" to achieve what was only claimed
2002-09-13 17:12:44 +05:30
-c,i,S act as toggles (not 'on' switches) for enhanced user flexibility
2002-06-19 05:15:30 +05:30
. no deprecated/illegal use of 'breakargv:' with goto
. bunched args are actually handled properly and none are ignored
. we tolerate NO whitespace and NO switches -- maybe too tolerant? */
static const char usage[] =
2005-06-22 00:50:39 +05:30
" -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]";
2002-05-30 09:14:46 +05:30
float tmp_delay = MAXFLOAT;
char *p;
2002-06-19 05:15:30 +05:30
while (*args) {
2002-11-29 04:39:48 +05:30
const char *cp = *(args++);
2002-05-30 09:14:46 +05:30
while (*cp) {
switch (*cp) {
case '\0':
case '-':
break;
case 'b':
Batch = 1;
break;
case 'c':
2002-06-19 05:15:30 +05:30
TOGw(Curwin, Show_CMDLIN);
2002-05-30 09:14:46 +05:30
break;
case 'd':
if (cp[1]) ++cp;
2002-06-19 05:15:30 +05:30
else if (*args) cp = *args++;
2002-05-30 09:14:46 +05:30
else std_err("-d requires argument");
/* a negative delay will be dealt with shortly... */
2004-07-07 00:43:38 +05:30
if (sscanf(cp, "%f", &tmp_delay) != 1)
2002-05-30 09:14:46 +05:30
std_err(fmtmk("bad delay '%s'", cp));
break;
2005-06-22 00:50:39 +05:30
case 'H':
TOGw(Curwin, Show_THREADS);
break;
case 'h':
2002-05-30 09:14:46 +05:30
case 'v': case 'V':
std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
2002-05-30 09:14:46 +05:30
case 'i':
2002-06-19 05:15:30 +05:30
TOGw(Curwin, Show_IDLEPS);
2002-11-30 06:52:01 +05:30
Curwin->rc.maxtasks = 0;
2002-05-30 09:14:46 +05:30
break;
case 'n':
if (cp[1]) cp++;
2002-06-19 05:15:30 +05:30
else if (*args) cp = *args++;
2002-05-30 09:14:46 +05:30
else std_err("-n requires argument");
2004-07-07 00:43:38 +05:30
if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1)
2002-05-30 09:14:46 +05:30
std_err(fmtmk("bad iterations arg '%s'", cp));
break;
case 'p':
do {
2003-09-08 04:58:49 +05:30
if (selection_type && selection_type != 'p') std_err("conflicting process selection");
2002-12-10 08:31:17 +05:30
selection_type = 'p';
2002-06-19 05:15:30 +05:30
if (cp[1]) cp++;
else if (*args) cp = *args++;
else std_err("-p argument missing");
2002-05-30 09:14:46 +05:30
if (Monpidsidx >= MONPIDMAX)
std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
2004-07-07 00:43:38 +05:30
if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0)
2002-05-30 09:14:46 +05:30
std_err(fmtmk("bad pid '%s'", cp));
if (!Monpids[Monpidsidx])
Monpids[Monpidsidx] = getpid();
Monpidsidx++;
if (!(p = strchr(cp, ',')))
break;
2002-06-19 05:15:30 +05:30
cp = p;
2002-05-30 09:14:46 +05:30
} while (*cp);
break;
case 's':
Secure_mode = 1;
break;
case 'S':
2002-06-19 05:15:30 +05:30
TOGw(Curwin, Show_CTIMES);
2002-05-30 09:14:46 +05:30
break;
2002-12-10 08:31:17 +05:30
case 'u':
do {
const char *errmsg;
2003-09-08 04:58:49 +05:30
if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
2002-12-10 08:31:17 +05:30
if (cp[1]) cp++;
else if (*args) cp = *args++;
else std_err("-u missing name");
errmsg = parse_uid(cp, &selection_uid);
if (errmsg) std_err(errmsg);
selection_type = 'u';
cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
} while(0);
break;
case 'U':
do {
const char *errmsg;
2003-09-08 07:09:49 +05:30
if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
2002-12-10 08:31:17 +05:30
if (cp[1]) cp++;
else if (*args) cp = *args++;
else std_err("-u missing name");
errmsg = parse_uid(cp, &selection_uid);
if (errmsg) std_err(errmsg);
selection_type = 'U';
cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
} while(0);
break;
2002-05-30 09:14:46 +05:30
default :
std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
, *cp, Myname, usage));
2002-06-19 05:15:30 +05:30
} /* end: switch (*cp) */
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
/* advance cp and jump over any numerical args used above */
2002-08-26 06:25:30 +05:30
if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1;
2002-06-19 05:15:30 +05:30
} /* end: while (*cp) */
} /* end: while (*args) */
2002-05-30 09:14:46 +05:30
/* fixup delay time, maybe... */
if (MAXFLOAT != tmp_delay) {
2004-07-07 00:43:38 +05:30
if (Secure_mode || tmp_delay < 0)
2002-05-30 09:14:46 +05:30
msg_save("Delay time Not changed");
else
2002-12-05 04:18:30 +05:30
Rc.delay_time = tmp_delay;
2002-05-30 09:14:46 +05:30
}
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
/*
* Set up the terminal attributes */
2002-06-19 05:15:30 +05:30
static void whack_terminal (void)
2002-02-02 04:17:29 +05:30
{
2002-05-30 09:14:46 +05:30
struct termios newtty;
2004-09-12 21:13:48 +05:30
if (Batch) {
setupterm("dumb", STDOUT_FILENO, NULL);
return;
}
2002-05-30 09:14:46 +05:30
setupterm(NULL, STDOUT_FILENO, NULL);
2004-09-12 21:13:48 +05:30
if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
std_err("failed tty get");
newtty = Savedtty;
newtty.c_lflag &= ~(ICANON | ECHO);
newtty.c_oflag &= ~(TAB3);
newtty.c_cc[VMIN] = 1;
newtty.c_cc[VTIME] = 0;
Ttychanged = 1;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
2002-05-30 09:14:46 +05:30
putp(Cap_clr_scr);
2004-09-12 21:13:48 +05:30
std_err(fmtmk("failed tty set: %s", strerror(errno)));
2002-05-30 09:14:46 +05:30
}
2004-09-12 21:13:48 +05:30
tcgetattr(STDIN_FILENO, &Rawtty);
#ifndef STDOUT_IOLBF
// thanks anyway stdio, but we'll manage buffering at the frame level...
setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf));
#endif
putp(Cap_clr_scr);
fflush(stdout);
2002-02-02 04:17:29 +05:30
}
2004-07-07 00:43:38 +05:30
2002-05-30 09:14:46 +05:30
/*###### Field Selection/Ordering routines #############################*/
2002-02-02 04:17:29 +05:30
2002-09-13 17:12:44 +05:30
2004-07-07 00:43:38 +05:30
// Display each field represented in the Fields Table along with its
// description and mark (with a leading asterisk) fields associated
// with upper case letter(s) in the passed 'fields string'.
//
// After all fields have been displayed, some extra explanatory
// text may also be output
2002-09-13 17:12:44 +05:30
static void display_fields (const char *fields, const char *xtra)
2002-02-02 04:17:29 +05:30
{
2002-06-19 05:15:30 +05:30
#define yRSVD 3
2002-09-13 17:12:44 +05:30
const char *p;
2002-06-19 05:15:30 +05:30
int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
2002-05-30 09:14:46 +05:30
2002-10-09 12:41:08 +05:30
/* we're relying on callers to first clear the screen and thus avoid screen
flicker if they're too lazy to handle their own asterisk (*) logic */
putp(Curwin->cap_bold);
2002-12-05 04:18:30 +05:30
for (i = 0; fields[i]; ++i) {
const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]);
int b = isupper(fields[i]);
2002-12-08 04:06:21 +05:30
if (!f) continue; // hey, should be std_err!
for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
2002-05-30 09:14:46 +05:30
;
2004-07-07 00:43:38 +05:30
PUTT("%s%s%c %c: %-10s = %s",
tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
b ? Curwin->cap_bold : Cap_norm,
b ? '*' : ' ',
fields[i],
p,
f->desc
);
2002-05-30 09:14:46 +05:30
}
2002-09-13 17:12:44 +05:30
if (xtra) {
putp(Curwin->capclr_rownorm);
while ((p = strchr(xtra, '\n'))) {
++i;
2004-07-07 00:43:38 +05:30
PUTT("%s%.*s",
tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
(int)(p - xtra),
xtra
);
2002-09-13 17:12:44 +05:30
xtra = ++p;
}
2002-05-30 09:14:46 +05:30
}
putp(Caps_off);
2002-06-19 05:15:30 +05:30
#undef yRSVD
2002-02-02 04:17:29 +05:30
}
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// Change order of displayed fields.
2002-05-30 09:14:46 +05:30
static void fields_reorder (void)
2002-02-02 04:17:29 +05:30
{
2002-10-10 03:02:33 +05:30
static const char prompt[] =
2002-10-02 05:40:30 +05:30
"Upper case letter moves field left, lower case right";
2002-05-30 09:14:46 +05:30
char c, *p;
int i;
putp(Cap_clr_scr);
putp(Cap_curs_huge);
2002-10-21 10:20:41 +05:30
for (;;) {
2002-12-05 04:18:30 +05:30
display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
show_special(1, fmtmk(FIELDS_current
2002-11-30 06:52:01 +05:30
, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2002-05-30 09:14:46 +05:30
chin(0, &c, 1);
2002-12-05 04:18:30 +05:30
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2002-05-30 09:14:46 +05:30
i = toupper(c) - 'A';
2002-11-30 06:52:01 +05:30
if (((p = strchr(Curwin->rc.fieldscur, i + 'A')))
|| ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) {
2002-05-30 09:14:46 +05:30
if (isupper(c)) p--;
2002-11-30 06:52:01 +05:30
if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) {
2002-05-30 09:14:46 +05:30
c = p[0];
p[0] = p[1];
p[1] = c;
}
}
2002-10-21 10:20:41 +05:30
}
2002-05-30 09:14:46 +05:30
putp(Cap_curs_norm);
2002-02-02 04:17:29 +05:30
}
2004-07-07 00:43:38 +05:30
// Select sort field.
2002-09-13 17:12:44 +05:30
static void fields_sort (void)
{
2002-10-10 03:02:33 +05:30
static const char prompt[] =
2002-09-13 17:12:44 +05:30
"Select sort field via field letter, type any other key to return";
char phoney[PFLAGSSIZ];
char c, *p;
2002-10-02 05:40:30 +05:30
int i, x;
2002-09-13 17:12:44 +05:30
strcpy(phoney, NUL_FIELDS);
2002-11-30 06:52:01 +05:30
x = i = Curwin->rc.sortindx;
putp(Cap_clr_scr);
putp(Cap_curs_huge);
2002-10-21 10:20:41 +05:30
for (;;) {
2002-09-13 17:12:44 +05:30
p = phoney + i;
*p = toupper(*p);
display_fields(phoney, SORT_xtra);
2004-07-07 00:43:38 +05:30
show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
2002-09-13 17:12:44 +05:30
chin(0, &c, 1);
2002-12-05 04:18:30 +05:30
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
2002-09-13 17:12:44 +05:30
i = toupper(c) - 'A';
*p = tolower(*p);
2002-10-02 05:40:30 +05:30
x = i;
2002-10-21 10:20:41 +05:30
}
2002-11-30 06:52:01 +05:30
if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
2002-10-02 05:40:30 +05:30
*p = x + 'A';
2002-11-30 06:52:01 +05:30
Curwin->rc.sortindx = x;
2002-09-13 17:12:44 +05:30
putp(Cap_curs_norm);
}
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// Toggle displayed fields.
2002-05-30 09:14:46 +05:30
static void fields_toggle (void)
2002-02-02 04:17:29 +05:30
{
2002-10-10 03:02:33 +05:30
static const char prompt[] =
2002-09-13 17:12:44 +05:30
"Toggle fields via field letter, type any other key to return";
2002-05-30 09:14:46 +05:30
char c, *p;
int i;
putp(Cap_clr_scr);
putp(Cap_curs_huge);
2002-10-21 10:20:41 +05:30
for (;;) {
2002-11-30 06:52:01 +05:30
display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
2004-07-07 00:43:38 +05:30
show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
2002-05-30 09:14:46 +05:30
chin(0, &c, 1);
2002-12-05 04:18:30 +05:30
if (!ft_get_ptr(FT_NEW_fmt, c)) break;
i = toupper(c) - 'A';
2002-11-30 06:52:01 +05:30
if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
2002-05-30 09:14:46 +05:30
*p = i + 'a';
2002-11-30 06:52:01 +05:30
else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
2002-05-30 09:14:46 +05:30
*p = i + 'A';
2002-10-21 10:20:41 +05:30
}
2002-05-30 09:14:46 +05:30
putp(Cap_curs_norm);
2002-02-02 04:17:29 +05:30
}
2002-06-19 05:15:30 +05:30
/*###### Windows/Field Groups support #################################*/
2002-02-02 04:17:29 +05:30
2004-07-07 00:43:38 +05:30
// For each of the four windows:
// 1) Set the number of fields/columns to display
// 2) Create the field columns heading
// 3) Set maximum cmdline length, if command lines are in use
// In the process, the required PROC_FILLxxx flags will be rebuilt!
static void reframewins (void)
2002-02-02 04:17:29 +05:30
{
WIN_t *w;
char *s;
2002-06-19 05:15:30 +05:30
const char *h;
int i, needpsdb = 0;
2002-05-30 09:14:46 +05:30
2002-12-08 04:06:21 +05:30
// Frames_libflags = 0; // should be called only when it's zero
// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible
w = Curwin;
do {
2002-12-05 04:18:30 +05:30
if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) {
// build window's procflags array and establish a tentative maxpflgs
2002-11-30 06:52:01 +05:30
for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) {
if (isupper(w->rc.fieldscur[i]))
w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A';
}
2002-06-19 05:15:30 +05:30
/* build a preliminary columns header not to exceed screen width
while accounting for a possible leading window number */
*(s = w->columnhdr) = '\0';
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) s = scat(s, " ");
for (i = 0; i < w->maxpflgs; i++) {
h = Fieldstab[w->procflags[i]].head;
// oops, won't fit -- we're outta here...
2004-07-15 06:47:15 +05:30
if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break;
s = scat(s, h);
}
2004-07-07 22:57:26 +05:30
// establish the final maxpflgs and prepare to grow the command column
// heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
// but that's ok because it won't be displayed anyway
w->maxpflgs = i;
2004-07-15 06:47:15 +05:30
w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
2004-07-07 22:57:26 +05:30
// finally, we can build the true run-time columns header, format the
// command column heading, if P_CMD is really being displayed, and
// rebuild the all-important PROC_FILLxxx flags that will be used
// until/if we're we're called again
*(s = w->columnhdr) = '\0';
2004-07-15 06:47:15 +05:30
// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
for (i = 0; i < w->maxpflgs; i++) {
2004-07-15 06:47:15 +05:30
int advance = (i==0) && !Rc.mode_altscr;
h = Fieldstab[w->procflags[i]].head;
if (P_WCH == w->procflags[i]) needpsdb = 1;
if (P_CMD == w->procflags[i]) {
2004-07-15 06:47:15 +05:30
s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ ));
2002-12-05 04:18:30 +05:30
if (CHKw(w, Show_CMDLIN)) {
2002-12-08 04:06:21 +05:30
Frames_libflags |= L_CMDLINE;
// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln;
2002-12-05 04:18:30 +05:30
}
} else
2004-07-15 06:47:15 +05:30
s = scat(s, h+advance);
2002-12-08 04:06:21 +05:30
Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
}
2004-07-15 06:47:15 +05:30
if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
}
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) w = w->next;
} while (w != Curwin);
2002-06-19 05:15:30 +05:30
// do we need the kernel symbol table (and is it already open?)
2002-06-19 05:15:30 +05:30
if (needpsdb) {
2004-07-07 00:43:38 +05:30
if (No_ksyms == -1) {
2002-06-19 05:15:30 +05:30
No_ksyms = 0;
if (open_psdb_message(NULL, msg_save))
No_ksyms = 1;
else
PSDBopen = 1;
}
}
2002-12-10 08:31:17 +05:30
if (selection_type=='U') Frames_libflags |= L_status;
2002-12-08 04:06:21 +05:30
if (Frames_libflags & L_EITHER) {
Frames_libflags &= ~L_EITHER;
if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
}
2002-12-08 04:06:21 +05:30
if (!Frames_libflags) Frames_libflags = L_DEFAULT;
2003-02-11 13:49:12 +05:30
if (selection_type=='p') Frames_libflags |= PROC_PID;
}
2004-07-07 00:43:38 +05:30
// Value a window's name and make the associated group name.
2002-06-19 05:15:30 +05:30
static void win_names (WIN_t *q, const char *name)
2002-02-02 04:17:29 +05:30
{
2002-11-30 06:52:01 +05:30
sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
2002-06-27 05:26:49 +05:30
sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
2002-02-02 04:17:29 +05:30
}
2004-07-07 00:43:38 +05:30
// Display a window/field group (ie. make it "current").
2002-10-09 12:41:08 +05:30
static void win_select (char ch)
2002-02-02 04:17:29 +05:30
{
2002-10-10 03:02:33 +05:30
static const char prompt[] = "Choose field group (1 - 4)";
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
/* if there's no ch, it means we're supporting the external interface,
2002-06-19 05:15:30 +05:30
so we must try to get our own darn ch by begging the user... */
if (!ch) {
show_pmt(prompt);
chin(0, (char *)&ch, 1);
2002-05-30 09:14:46 +05:30
}
2002-06-19 05:15:30 +05:30
switch (ch) {
case 'a': /* we don't carry 'a' / 'w' in our */
2002-06-27 05:26:49 +05:30
Curwin = Curwin->next; /* pmt - they're here for a good */
break; /* friend of ours -- wins_colors. */
case 'w': /* (however those letters work via */
2002-06-27 05:26:49 +05:30
Curwin = Curwin->prev; /* the pmt too but gee, end-loser */
break; /* should just press the darn key) */
case '1': case '2':
case '3': case '4':
2002-12-05 08:30:42 +05:30
Curwin = &Winstk[ch - '1'];
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
}
2002-02-02 04:17:29 +05:30
}
2004-07-07 00:43:38 +05:30
// Just warn the user when a command can't be honored.
2002-06-19 05:15:30 +05:30
static int win_warn (void)
2002-05-30 09:14:46 +05:30
{
2004-07-07 00:43:38 +05:30
show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname));
// we gotta' return false 'cause we're somewhat well known within
// macro society, by way of that sassy little tertiary operator...
2002-06-19 05:15:30 +05:30
return 0;
2002-05-30 09:14:46 +05:30
}
2004-07-07 00:43:38 +05:30
// Change colors *Helper* function to save/restore settings;
// ensure colors will show; and rebuild the terminfo strings.
2002-11-08 06:01:28 +05:30
static void winsclrhlp (WIN_t *q, int save)
2002-05-30 09:14:46 +05:30
{
2002-06-19 05:15:30 +05:30
static int flgssav, summsav, msgssav, headsav, tasksav;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
if (save) {
2002-11-30 06:52:01 +05:30
flgssav = q->rc.winflags; summsav = q->rc.summclr;
msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr;
2002-06-19 05:15:30 +05:30
SETw(q, Show_COLORS);
} else {
2002-11-30 06:52:01 +05:30
q->rc.winflags = flgssav; q->rc.summclr = summsav;
q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav;
2002-05-30 09:14:46 +05:30
}
2002-06-19 05:15:30 +05:30
capsmk(q);
2002-05-30 09:14:46 +05:30
}
2004-07-07 00:43:38 +05:30
// Change colors used in display
2002-06-19 05:15:30 +05:30
static void wins_colors (void)
2002-05-30 09:14:46 +05:30
{
2002-06-19 05:15:30 +05:30
#define kbdABORT 'q'
#define kbdAPPLY '\n'
2002-11-30 06:52:01 +05:30
int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
2002-06-19 05:15:30 +05:30
char ch, tgt = 'T';
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
if (0 >= max_colors) {
show_msg("\aNo colors to map!");
return;
}
2002-11-08 06:01:28 +05:30
winsclrhlp(Curwin, 1);
putp(Cap_clr_scr);
putp(Cap_curs_huge);
2002-06-19 05:15:30 +05:30
do {
putp(Cap_home);
2002-06-19 05:15:30 +05:30
/* this string is well above ISO C89's minimum requirements! */
2004-07-07 00:43:38 +05:30
show_special(
1,
fmtmk(
COLOR_help,
procps_version,
Curwin->grpname,
CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
CHKw(Curwin, Show_COLORS) ? "On" : "Off",
CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
tgt,
clr,
Curwin->grpname
)
);
2002-06-19 05:15:30 +05:30
chin(0, &ch, 1);
switch (ch) {
case 'S':
2002-11-30 06:52:01 +05:30
pclr = &Curwin->rc.summclr;
2002-06-19 05:15:30 +05:30
clr = *pclr;
tgt = ch;
break;
case 'M':
2002-11-30 06:52:01 +05:30
pclr = &Curwin->rc.msgsclr;
2002-06-19 05:15:30 +05:30
clr = *pclr;
tgt = ch;
break;
case 'H':
2002-11-30 06:52:01 +05:30
pclr = &Curwin->rc.headclr;
2002-06-19 05:15:30 +05:30
clr = *pclr;
tgt = ch;
break;
case 'T':
2002-11-30 06:52:01 +05:30
pclr = &Curwin->rc.taskclr;
2002-06-19 05:15:30 +05:30
clr = *pclr;
tgt = ch;
break;
case '0' ... '7':
2002-06-19 05:15:30 +05:30
clr = ch - '0';
*pclr = clr;
break;
case 'B':
TOGw(Curwin, View_NOBOLD);
break;
2002-06-19 05:15:30 +05:30
case 'b':
TOGw(Curwin, Show_HIBOLD);
break;
case 'z':
TOGw(Curwin, Show_COLORS);
break;
case 'a':
case 'w':
win_select(ch);
2002-11-08 06:01:28 +05:30
winsclrhlp(Curwin, 1);
2002-11-30 06:52:01 +05:30
clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
2002-06-19 05:15:30 +05:30
tgt = 'T';
break;
}
capsmk(Curwin);
} while (kbdAPPLY != ch && kbdABORT != ch);
if (kbdABORT == ch)
2002-11-08 06:01:28 +05:30
winsclrhlp(Curwin, 0);
2002-06-19 05:15:30 +05:30
putp(Cap_curs_norm);
#undef kbdABORT
#undef kbdAPPLY
}
2004-07-07 00:43:38 +05:30
// Manipulate flag(s) for all our windows.
2002-06-19 05:15:30 +05:30
static void wins_reflag (int what, int flg)
{
WIN_t *w;
w = Curwin;
do {
switch (what) {
2002-06-27 05:26:49 +05:30
case Flags_TOG:
2002-06-19 05:15:30 +05:30
TOGw(w, flg);
break;
2002-06-27 05:26:49 +05:30
case Flags_SET: /* Ummmm, i can't find anybody */
2002-10-02 05:40:30 +05:30
SETw(w, flg); /* who uses Flags_set ... */
break;
2002-06-27 05:26:49 +05:30
case Flags_OFF:
2002-06-19 05:15:30 +05:30
OFFw(w, flg);
break;
}
2004-07-07 00:43:38 +05:30
// a flag with special significance -- user wants to rebalance
// display so we gotta' 'off' one number then force on two flags...
2002-10-02 05:40:30 +05:30
if (EQUWINS_cwo == flg) {
2002-11-30 06:52:01 +05:30
w->rc.maxtasks = 0;
2002-06-19 05:15:30 +05:30
SETw(w, Show_IDLEPS | VISIBLE_tsk);
2002-10-02 05:40:30 +05:30
}
w = w->next;
} while (w != Curwin);
2002-06-19 05:15:30 +05:30
}
2004-07-07 00:43:38 +05:30
// Set the screen dimensions and arrange for the real workhorse.
// (also) catches:
// SIGWINCH and SIGCONT
2002-06-19 05:15:30 +05:30
static void wins_resize (int dont_care_sig)
{
struct winsize wz;
char *env_columns; // Unix98 environment variable COLUMNS
char *env_lines; // Unix98 environment variable LINES
2002-06-19 05:15:30 +05:30
2002-10-12 11:06:52 +05:30
(void)dont_care_sig;
Screen_cols = columns; // <term.h>
Screen_rows = lines; // <term.h>
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) {
2002-06-19 05:15:30 +05:30
Screen_cols = wz.ws_col;
Screen_rows = wz.ws_row;
}
if (Batch) Screen_rows = MAXINT;
2002-10-21 10:20:41 +05:30
env_columns = getenv("COLUMNS");
if(env_columns && *env_columns){
long t;
char *endptr;
t = strtol(env_columns, &endptr, 0);
if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
}
env_lines = getenv("LINES");
if(env_lines && *env_lines){
long t;
char *endptr;
t = strtol(env_lines, &endptr, 0);
if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
}
// be crudely tolerant of crude tty emulators
if (avoid_last_column) Screen_cols--;
// we might disappoint some folks (but they'll deserve it)
if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
2002-11-27 05:54:01 +05:30
2004-07-07 00:43:38 +05:30
// keep our support for output optimization in sync with current reality
// note: when we're in Batch mode, we don't really need a Pseudo_scrn and
// when not Batch, our buffer will contain 1 extra 'line' since
// Msg_row is never represented -- but it's nice to have some space
// between us and the great-beyond...
2002-11-27 05:54:01 +05:30
Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
if (Batch) Pseudo_size = ROWBUFSIZ + 1;
2004-07-07 00:43:38 +05:30
else Pseudo_size = Pseudo_cols * Screen_rows;
2002-11-27 05:54:01 +05:30
Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size);
// force rebuild of column headers AND libproc/readproc requirements
2002-12-08 04:06:21 +05:30
Frames_libflags = 0;
2003-03-18 05:12:00 +05:30
ZAP_TIMEOUT
2002-06-19 05:15:30 +05:30
}
2004-07-07 00:43:38 +05:30
// Set up the raw/incomplete field group windows --
// they'll be finished off after startup completes.
// [ and very likely that will override most/all of our efforts ]
// [ --- life-is-NOT-fair --- ]
2002-06-19 05:15:30 +05:30
static void windows_stage1 (void)
{
WIN_t *w;
2002-11-08 06:01:28 +05:30
int i;
2002-06-19 05:15:30 +05:30
for (i = 0; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
w = &Winstk[i];
2002-06-27 05:26:49 +05:30
w->winnum = i + 1;
2002-12-05 04:18:30 +05:30
w->rc = Rc.win[i];
2002-06-19 05:15:30 +05:30
w->captab[0] = Cap_norm;
w->captab[1] = Cap_norm;
w->captab[2] = w->cap_bold;
2002-11-08 06:01:28 +05:30
w->captab[3] = w->capclr_sum;
w->captab[4] = w->capclr_msg;
w->captab[5] = w->capclr_pmt;
w->captab[6] = w->capclr_hdr;
w->captab[7] = w->capclr_rowhigh;
w->captab[8] = w->capclr_rownorm;
2002-06-19 05:15:30 +05:30
w->next = w + 1;
w->prev = w - 1;
++w;
}
/* fixup the circular chains... */
2002-12-05 08:30:42 +05:30
Winstk[3].next = &Winstk[0];
Winstk[0].prev = &Winstk[3];
Curwin = Winstk;
2002-06-19 05:15:30 +05:30
}
2004-07-07 00:43:38 +05:30
// This guy just completes the field group windows after the
// rcfiles have been read and command line arguments parsed
2002-06-19 05:15:30 +05:30
static void windows_stage2 (void)
{
int i;
for (i = 0; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
win_names(&Winstk[i], Winstk[i].rc.winname);
capsmk(&Winstk[i]);
2002-06-19 05:15:30 +05:30
}
// rely on this next guy to force a call (eventually) to reframewins
wins_resize(0);
2002-06-19 05:15:30 +05:30
}
2004-07-07 00:43:38 +05:30
2002-11-08 06:01:28 +05:30
/*###### Main Screen routines ##########################################*/
2002-06-19 05:15:30 +05:30
2004-07-07 00:43:38 +05:30
// Process keyboard input during the main loop
2002-11-08 06:01:28 +05:30
static void do_key (unsigned c)
2002-06-19 05:15:30 +05:30
{
2002-11-08 06:01:28 +05:30
// standardized 'secure mode' errors
static const char err_secure[] = "\aUnavailable in secure mode";
#ifdef WARN_NOT_SMP
// standardized 'smp' errors
static const char err_smp[] = "\aSorry, only 1 cpu detected";
#endif
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
switch (c) {
case '1':
#ifdef WARN_NOT_SMP
if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
else show_msg(err_smp);
#else
TOGw(Curwin, View_CPUSUM);
#endif
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'a':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) Curwin = Curwin->next;
2002-11-08 06:01:28 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'A':
2002-12-05 04:18:30 +05:30
Rc.mode_altscr = !Rc.mode_altscr;
2002-11-08 06:01:28 +05:30
wins_resize(0);
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'b':
if (VIZCHKc) {
if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
2002-11-08 06:01:28 +05:30
show_msg("\aNothing to highlight!");
else {
TOGw(Curwin, Show_HIBOLD);
capsmk(Curwin);
}
}
break;
2002-05-30 09:14:46 +05:30
case 'B':
TOGw(Curwin, View_NOBOLD);
capsmk(Curwin);
break;
2002-11-08 06:01:28 +05:30
case 'c':
VIZTOGc(Show_CMDLIN);
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'd':
case 's':
if (Secure_mode)
show_msg(err_secure);
else {
float tmp =
2002-12-05 04:18:30 +05:30
get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
2004-07-07 00:43:38 +05:30
if (tmp > -1) Rc.delay_time = tmp;
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'f':
if (VIZCHKc) fields_toggle();
2002-11-08 06:01:28 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'F':
case 'O':
if (VIZCHKc) fields_sort();
2002-11-08 06:01:28 +05:30
break;
2002-10-13 13:53:50 +05:30
2002-11-08 06:01:28 +05:30
case 'g':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) {
2002-11-08 06:01:28 +05:30
char tmp[GETBUFSIZ];
2004-07-07 00:43:38 +05:30
strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname)));
2002-11-08 06:01:28 +05:30
if (tmp[0]) win_names(Curwin, tmp);
}
break;
2002-05-30 09:14:46 +05:30
2002-11-08 06:01:28 +05:30
case 'G':
win_select(0);
break;
2002-06-19 05:15:30 +05:30
2005-06-22 00:50:39 +05:30
case 'H':
if (VIZCHKc) {
TOGw(Curwin, Show_THREADS);
show_msg(fmtmk("Show threads %s"
, CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
}
break;
2002-11-08 06:01:28 +05:30
case 'h':
case '?':
{ char ch;
putp(Cap_clr_scr);
putp(Cap_curs_huge);
/* this string is well above ISO C89's minimum requirements! */
2004-07-07 00:43:38 +05:30
show_special(
1,
fmtmk(
KEYS_help,
procps_version,
Curwin->grpname,
CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
Rc.delay_time,
Secure_mode ? "On" : "Off",
Secure_mode ? "" : KEYS_help_unsecured
)
);
2002-11-08 06:01:28 +05:30
chin(0, &ch, 1);
if ('?' == ch || 'h' == ch) {
do {
putp(Cap_clr_scr);
show_special(1, fmtmk(WINDOWS_help
2002-11-08 06:01:28 +05:30
, Curwin->grpname
2002-12-05 08:30:42 +05:30
, Winstk[0].rc.winname
, Winstk[1].rc.winname
, Winstk[2].rc.winname
, Winstk[3].rc.winname));
2002-11-08 06:01:28 +05:30
chin(0, &ch, 1);
win_select(ch);
} while ('\n' != ch);
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
putp(Cap_curs_norm);
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
break;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
case 'i':
VIZTOGc(Show_IDLEPS);
break;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
case 'I':
#ifdef WARN_NOT_SMP
if (Cpu_tot > 1) {
2002-12-05 04:18:30 +05:30
Rc.mode_irixps = !Rc.mode_irixps;
show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2002-11-08 06:01:28 +05:30
} else
show_msg(err_smp);
#else
2002-12-05 04:18:30 +05:30
Rc.mode_irixps = !Rc.mode_irixps;
show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
2002-11-08 06:01:28 +05:30
#endif
break;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
case 'k':
if (Secure_mode) {
show_msg(err_secure);
} else {
int sig, pid = get_int("PID to kill");
2004-07-07 00:43:38 +05:30
if (pid > 0) {
2002-11-08 06:01:28 +05:30
sig = signal_name_to_number(
2004-07-07 00:43:38 +05:30
ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL)));
if (sig == -1) sig = DEF_SIGNAL;
2002-11-08 06:01:28 +05:30
if (sig && kill(pid, sig))
2004-07-07 00:43:38 +05:30
show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno)));
2002-11-08 06:01:28 +05:30
}
}
break;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
case 'l':
TOGw(Curwin, View_LOADAV);
break;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
case 'm':
TOGw(Curwin, View_MEMORY);
break;
2002-06-19 05:15:30 +05:30
case 'n':
case '#':
if (VIZCHKc) {
2002-11-08 06:01:28 +05:30
int num =
2004-07-07 00:43:38 +05:30
get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
if (num > -1) Curwin->rc.maxtasks = num;
2002-06-19 05:15:30 +05:30
}
break;
case 'o':
if (VIZCHKc) fields_reorder();
2002-06-19 05:15:30 +05:30
break;
2002-09-13 17:12:44 +05:30
case 'q':
end_pgm(0);
2002-09-13 17:12:44 +05:30
2002-06-19 05:15:30 +05:30
case 'r':
if (Secure_mode)
2002-10-02 05:40:30 +05:30
show_msg(err_secure);
2002-06-19 05:15:30 +05:30
else {
2002-11-08 06:01:28 +05:30
int val, pid = get_int("PID to renice");
2004-07-07 00:43:38 +05:30
if (pid > 0) {
2002-11-08 06:01:28 +05:30
val = get_int(fmtmk("Renice PID %d to value", pid));
if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
2004-07-07 00:43:38 +05:30
show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno)));
2002-11-08 06:01:28 +05:30
}
2002-06-19 05:15:30 +05:30
}
break;
case 'R':
VIZTOGc(Qsrt_NORMAL);
break;
case 'S':
if (VIZCHKc) {
TOGw(Curwin, Show_CTIMES);
2004-07-07 00:43:38 +05:30
show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
2002-06-19 05:15:30 +05:30
}
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
case 't':
TOGw(Curwin, View_STATES);
break;
2002-05-30 09:14:46 +05:30
2002-12-10 08:31:17 +05:30
// case 'u':
// if (VIZCHKc)
// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
// break;
2002-06-19 05:15:30 +05:30
case 'u':
2002-12-10 08:31:17 +05:30
// if (!VIZCHKc) break;
do {
const char *errmsg;
const char *answer;
answer = ask4str("Which user (blank for all)");
// FIXME: do this better:
if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
selection_type = 0;
selection_uid = -1;
break;
}
errmsg = parse_uid(answer, &selection_uid);
if (errmsg) {
show_msg(errmsg);
// Change settings here? I guess not.
break;
}
selection_type = 'u';
} while(0);
break;
case 'U':
// if (!VIZCHKc) break;
do {
const char *errmsg;
const char *answer;
answer = ask4str("Which user (blank for all)");
// FIXME: do this better:
if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
selection_type = 0;
selection_uid = -1;
break;
}
errmsg = parse_uid(answer, &selection_uid);
if (errmsg) {
show_msg(errmsg);
// Change settings here? I guess not.
break;
}
selection_type = 'U';
} while(0);
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-09-13 17:12:44 +05:30
case 'w':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) Curwin = Curwin->prev;
2002-09-13 17:12:44 +05:30
break;
2002-06-19 05:15:30 +05:30
case 'W':
2002-12-05 04:18:30 +05:30
{ const char *err = rc_write_whatever();
2002-11-30 21:26:53 +05:30
if (err)
2002-12-05 04:18:30 +05:30
show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
2002-11-30 21:26:53 +05:30
else
2002-12-05 04:18:30 +05:30
show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
2002-05-30 09:14:46 +05:30
}
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
case 'x':
if (VIZCHKc) {
TOGw(Curwin, Show_HICOLS);
capsmk(Curwin);
}
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
case 'y':
if (VIZCHKc) {
TOGw(Curwin, Show_HIROWS);
capsmk(Curwin);
}
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
case 'z':
if (VIZCHKc) {
TOGw(Curwin, Show_COLORS);
capsmk(Curwin);
}
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
case 'Z':
wins_colors();
break;
2002-05-30 09:14:46 +05:30
2002-10-02 05:40:30 +05:30
case '-':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
2002-09-13 17:12:44 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-10-02 05:40:30 +05:30
case '_':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-10-02 05:40:30 +05:30
case '=':
2002-11-30 06:52:01 +05:30
Curwin->rc.maxtasks = 0;
2002-09-13 17:12:44 +05:30
SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
Monpidsidx = 0;
selection_type = '\0';
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-10-02 05:40:30 +05:30
case '+':
2002-12-05 04:18:30 +05:30
if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-09-13 17:12:44 +05:30
case '<':
if (VIZCHKc) {
2002-12-05 04:18:30 +05:30
FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1;
2002-11-30 06:52:01 +05:30
while (*p != Curwin->rc.sortindx) --p;
2002-10-13 23:53:22 +05:30
if (--p >= Curwin->procflags)
2002-11-30 06:52:01 +05:30
Curwin->rc.sortindx = *p;
2002-09-13 17:12:44 +05:30
}
2002-06-19 05:15:30 +05:30
break;
2002-09-13 17:12:44 +05:30
case '>':
if (VIZCHKc) {
2002-12-05 04:18:30 +05:30
FLG_t *p = Curwin->procflags;
2002-11-30 06:52:01 +05:30
while (*p != Curwin->rc.sortindx) ++p;
2002-10-13 23:53:22 +05:30
if (++p < Curwin->procflags + Curwin->maxpflgs)
2002-11-30 06:52:01 +05:30
Curwin->rc.sortindx = *p;
2002-09-13 17:12:44 +05:30
}
2002-06-19 05:15:30 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-11-29 21:31:04 +05:30
case 'M': // these keys represent old-top compatability
case 'N': // -- grouped here so that if users could ever
case 'P': // be weaned, we would just whack this part...
case 'T':
{ static struct {
const char *xmsg;
2004-02-24 01:01:00 +05:30
const unsigned xkey;
2002-12-05 04:18:30 +05:30
const FLG_t sort;
2002-11-29 21:31:04 +05:30
} xtab[] = {
2004-02-24 01:01:00 +05:30
{ "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
{ "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, };
2002-11-29 21:31:04 +05:30
int i;
for (i = 0; i < MAXTBL(xtab); ++i)
if (c == xtab[i].xkey) {
2002-11-30 06:52:01 +05:30
Curwin->rc.sortindx = xtab[i].sort;
// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg));
2002-11-29 21:31:04 +05:30
break;
}
}
break;
case '\n': // just ignore these, they'll have the effect
case ' ': // of refreshing display after waking us up !
2002-09-13 17:12:44 +05:30
break;
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
default:
show_msg("\aUnknown command - try 'h' for help");
}
2004-07-07 00:43:38 +05:30
// The following assignment will force a rebuild of all column headers and
// the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are
// some keys that COULD require new column headers and/or libproc flags:
// 'A' - likely
// 'c' - likely when !Mode_altscr, maybe when Mode_altscr
// 'F' - maybe, if new field forced on
// 'f' - likely
// 'G' - likely
// 'O' - maybe, if new field forced on
// 'o' - maybe, if new field brought into view
// 'Z' - likely, if 'Curwin' changed when !Mode_altscr
// '-' - likely (restricted to Mode_altscr)
// '_' - likely (restricted to Mode_altscr)
// '=' - maybe, but only when Mode_altscr
// '+' - likely (restricted to Mode_altscr)
// ( At this point we have a human being involved and so have all the time )
// ( in the world. We can afford a few extra cpu cycles every now & then! )
2002-12-08 04:06:21 +05:30
Frames_libflags = 0;
2002-05-30 09:14:46 +05:30
}
2004-07-07 00:43:38 +05:30
// State display *Helper* function to calc and display the state
// percentages for a single cpu. In this way, we can support
// the following environments without the usual code bloat.
// 1) single cpu machines
// 2) modest smp boxes with room for each cpu's percentages
// 3) massive smp guys leaving little or no room for process
// display and thus requiring the cpu summary toggle
2002-12-05 04:18:30 +05:30
static void summaryhlp (CPU_t *cpu, const char *pfx)
2002-11-08 06:01:28 +05:30
{
2004-07-07 00:43:38 +05:30
// we'll trim to zero if we get negative time ticks,
// which has happened with some SMP kernels (pre-2.4?)
2002-12-05 04:18:30 +05:30
#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
2003-09-08 07:09:49 +05:30
SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, tot_frme, tz;
2002-11-08 06:01:28 +05:30
float scale;
2002-11-27 14:43:42 +05:30
u_frme = cpu->u - cpu->u_sav;
s_frme = cpu->s - cpu->s_sav;
n_frme = cpu->n - cpu->n_sav;
2002-11-08 06:01:28 +05:30
i_frme = TRIMz(cpu->i - cpu->i_sav);
2002-11-27 14:43:42 +05:30
w_frme = cpu->w - cpu->w_sav;
2003-09-08 07:09:49 +05:30
x_frme = cpu->x - cpu->x_sav;
y_frme = cpu->y - cpu->y_sav;
tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme;
2004-07-07 00:43:38 +05:30
if (tot_frme < 1) tot_frme = 1;
2002-11-08 06:01:28 +05:30
scale = 100.0 / (float)tot_frme;
2004-07-07 00:43:38 +05:30
// display some kinda' cpu state percentages
// (who or what is explained by the passed prefix)
show_special(
0,
fmtmk(
States_fmts,
pfx,
(float)u_frme * scale,
(float)s_frme * scale,
(float)n_frme * scale,
(float)i_frme * scale,
(float)w_frme * scale,
(float)x_frme * scale,
(float)y_frme * scale
)
);
2002-11-08 06:01:28 +05:30
Msg_row += 1;
// remember for next time around
cpu->u_sav = cpu->u;
cpu->s_sav = cpu->s;
cpu->n_sav = cpu->n;
cpu->i_sav = cpu->i;
cpu->w_sav = cpu->w;
2003-09-08 07:09:49 +05:30
cpu->x_sav = cpu->x;
cpu->y_sav = cpu->y;
2002-11-08 06:01:28 +05:30
#undef TRIMz
}
2004-07-07 00:43:38 +05:30
// Begin a new frame by:
// 1) Refreshing the all important proc table
// 2) Displaying uptime and load average (maybe)
// 3) Displaying task/cpu states (maybe)
// 4) Displaying memory & swap usage (maybe)
// and then, returning a pointer to the pointers to the proc_t's!
2002-11-08 06:01:28 +05:30
static proc_t **summary_show (void)
2002-05-30 09:14:46 +05:30
{
static proc_t **p_table = NULL;
2002-12-05 04:18:30 +05:30
static CPU_t *smpcpu = NULL;
2002-05-30 09:14:46 +05:30
2002-11-08 09:04:20 +05:30
// whoa first time, gotta' prime the pump...
2002-05-30 09:14:46 +05:30
if (!p_table) {
2003-02-11 13:49:12 +05:30
p_table = procs_refresh(NULL, Frames_libflags);
2002-06-19 05:15:30 +05:30
putp(Cap_clr_scr);
putp(Cap_rmam);
2003-03-18 05:12:00 +05:30
#ifndef PROF
// sleep for half a second
tv.tv_sec = 0;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv); // ought to loop until done
#endif
2004-07-07 00:43:38 +05:30
} else {
2002-06-19 05:15:30 +05:30
putp(Batch ? "\n\n" : Cap_home);
2004-07-07 00:43:38 +05:30
}
2002-12-08 04:06:21 +05:30
p_table = procs_refresh(p_table, Frames_libflags);
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// Display Uptime and Loadavg
2002-06-19 05:15:30 +05:30
if (CHKw(Curwin, View_LOADAV)) {
2003-03-18 05:12:00 +05:30
if (!Rc.mode_altscr) {
show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));
2003-03-18 05:12:00 +05:30
} else {
2004-07-07 00:43:38 +05:30
show_special(
0,
fmtmk(
CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
Curwin->grpname,
sprint_uptime()
)
);
2003-03-18 05:12:00 +05:30
}
2002-06-19 05:15:30 +05:30
Msg_row += 1;
}
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// Display Task and Cpu(s) States
2002-11-08 06:01:28 +05:30
if (CHKw(Curwin, View_STATES)) {
2004-07-07 00:43:38 +05:30
show_special(
0,
fmtmk(
STATES_line1,
Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
)
);
2002-11-08 06:01:28 +05:30
Msg_row += 1;
smpcpu = cpus_refresh(smpcpu);
if (CHKw(Curwin, View_CPUSUM)) {
// display just the 1st /proc/stat line
summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
} else {
int i;
char tmp[SMLBUFSIZ];
// display each cpu's states separately
for (i = 0; i < Cpu_tot; i++) {
2004-07-07 05:41:19 +05:30
snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);
summaryhlp(&smpcpu[i], tmp);
}
2002-11-08 06:01:28 +05:30
}
}
2002-06-19 05:15:30 +05:30
2004-07-07 00:43:38 +05:30
// Display Memory and Swap stats
2002-11-08 06:01:28 +05:30
meminfo();
if (CHKw(Curwin, View_MEMORY)) {
show_special(0, fmtmk(MEMORY_line1
2002-11-08 06:01:28 +05:30
, kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));
show_special(0, fmtmk(MEMORY_line2
2002-11-08 06:01:28 +05:30
, kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));
Msg_row += 2;
}
2002-06-19 05:15:30 +05:30
SETw(Curwin, NEWFRAM_cwo);
return p_table;
2002-11-08 06:01:28 +05:30
}
2004-07-07 22:57:26 +05:30
#define PAGES_TO_KB(n) (unsigned)( (n) << page_to_kb_shift )
// the following macro is our means to 'inline' emitting a column -- next to
// procs_refresh, that's the most frequent and costly part of top's job !
#define MKCOL(va...) do { \
if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \
snprintf(cbuf, sizeof(cbuf), f, ## va); \
} else { \
snprintf(_z, sizeof(_z), f, ## va); \
snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
q->capclr_rowhigh, \
_z, \
!(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
); \
pad += q->len_rowhigh; \
if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
} \
} while (0)
2003-09-06 10:41:04 +05:30
2004-07-07 00:43:38 +05:30
// Display information for a single task row.
2002-11-29 04:39:48 +05:30
static void task_show (const WIN_t *q, const proc_t *p)
2002-11-08 06:01:28 +05:30
{
2004-07-15 06:47:15 +05:30
char rbuf[ROWBUFSIZ];
char *rp = rbuf;
2002-11-08 06:01:28 +05:30
int j, x, pad;
2004-07-15 06:47:15 +05:30
*rp = '\0';
pad = Rc.mode_altscr;
// if (pad) rp = scat(rp, " ");
2002-11-08 06:01:28 +05:30
for (x = 0; x < q->maxpflgs; x++) {
char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ];
2002-12-05 04:18:30 +05:30
FLG_t i = q->procflags[x]; // support for our field/column
2002-11-29 04:39:48 +05:30
const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt
unsigned s = Fieldstab[i].scale; // string must be altered !
2002-11-27 05:54:01 +05:30
unsigned w = Fieldstab[i].width;
2004-07-15 06:47:15 +05:30
int advance = (x==0) && !Rc.mode_altscr;
2002-11-08 06:01:28 +05:30
switch (i) {
case P_CMD:
{ char tmp[ROWBUFSIZ];
unsigned flags;
2004-11-05 02:20:59 +05:30
int maxcmd = q->maxcmdln;
if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
else flags = ESC_DEFUNCT;
2004-11-05 02:20:59 +05:30
escape_command(tmp, p, sizeof tmp, &maxcmd, flags);
MKCOL(q->maxcmdln, q->maxcmdln, tmp);
}
break;
2002-11-08 06:01:28 +05:30
case P_COD:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_CPN:
MKCOL((unsigned)p->processor);
2002-11-08 06:01:28 +05:30
break;
case P_CPU:
{ float u = (float)p->pcpu * Frame_tscale;
2005-01-06 05:43:12 +05:30
if (u > pcpu_max_value) u = pcpu_max_value;
MKCOL(u);
2002-11-08 06:01:28 +05:30
}
break;
case P_DAT:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_DRT:
MKCOL(scale_num((unsigned)p->dt, w, s));
2002-11-08 06:01:28 +05:30
break;
case P_FLG:
{ char tmp[TNYBUFSIZ];
2002-11-29 04:39:48 +05:30
snprintf(tmp, sizeof(tmp), f, (long)p->flags);
for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.';
2002-11-29 04:39:48 +05:30
f = tmp;
2002-11-30 00:19:32 +05:30
MKCOL("");
}
2002-11-08 06:01:28 +05:30
break;
case P_FLT:
MKCOL(scale_num(p->maj_flt, w, s));
2002-11-08 06:01:28 +05:30
break;
case P_GRP:
MKCOL(p->egroup);
2002-11-08 06:01:28 +05:30
break;
case P_MEM:
2004-07-07 22:57:26 +05:30
MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
2002-11-08 06:01:28 +05:30
break;
case P_NCE:
MKCOL((int)p->nice);
2002-11-08 06:01:28 +05:30
break;
case P_PID:
2003-09-20 13:59:55 +05:30
MKCOL((unsigned)p->XXXID);
2002-11-08 06:01:28 +05:30
break;
case P_PPD:
MKCOL((unsigned)p->ppid);
2002-11-08 06:01:28 +05:30
break;
case P_PRI:
2002-12-09 04:21:09 +05:30
if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
2004-07-15 06:47:15 +05:30
f = " RT";
2002-11-30 00:19:32 +05:30
MKCOL("");
2002-11-29 04:39:48 +05:30
} else
MKCOL((int)p->priority);
2002-11-08 06:01:28 +05:30
break;
case P_RES:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_SHR:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_STA:
MKCOL(p->state);
2002-11-08 06:01:28 +05:30
break;
case P_SWP:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_TME:
case P_TM2:
2002-12-05 04:18:30 +05:30
{ TIC_t t = p->utime + p->stime;
2002-11-08 06:01:28 +05:30
if (CHKw(q, Show_CTIMES))
t += (p->cutime + p->cstime);
MKCOL(scale_tics(t, w));
2002-11-08 06:01:28 +05:30
}
break;
case P_TTY:
{ char tmp[TNYBUFSIZ];
2003-09-20 13:59:55 +05:30
dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
MKCOL(tmp);
2002-11-08 06:01:28 +05:30
}
break;
case P_UID:
MKCOL((unsigned)p->euid);
2002-11-08 06:01:28 +05:30
break;
2002-12-06 12:23:29 +05:30
case P_URE:
MKCOL(p->euser);
2002-11-08 06:01:28 +05:30
break;
2002-12-06 12:23:29 +05:30
case P_URR:
MKCOL(p->ruser);
break;
2002-11-08 06:01:28 +05:30
case P_VRT:
2004-07-07 22:57:26 +05:30
MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
2002-11-08 06:01:28 +05:30
break;
case P_WCH:
if (No_ksyms) {
2004-07-15 06:47:15 +05:30
f = " %08lx ";
2002-11-29 04:39:48 +05:30
MKCOL((long)p->wchan);
2002-11-08 06:01:28 +05:30
} else {
MKCOL(lookup_wchan(p->wchan, p->XXXID));
2002-11-08 06:01:28 +05:30
}
break;
2002-11-08 06:01:28 +05:30
} /* end: switch 'procflag' */
2004-07-15 06:47:15 +05:30
rp = scat(rp, cbuf+advance);
2002-11-08 06:01:28 +05:30
} /* end: for 'maxpflgs' */
2004-07-07 00:43:38 +05:30
PUFF(
"\n%s%.*s%s%s",
(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
Screen_cols + pad,
rbuf,
Caps_off,
2004-07-15 06:47:15 +05:30
"" /*Cap_clr_eol*/
2004-07-07 00:43:38 +05:30
);
2002-11-08 06:01:28 +05:30
#undef MKCOL
2002-06-19 05:15:30 +05:30
}
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
// Squeeze as many tasks as we can into a single window,
// after sorting the passed proc table.
2002-11-08 06:01:28 +05:30
static void window_show (proc_t **ppt, WIN_t *q, int *lscr)
2002-06-19 05:15:30 +05:30
{
#ifdef SORT_SUPRESS
2002-11-08 06:01:28 +05:30
// the 1 flag that DOES and 2 flags that MAY impact our proc table qsort
2002-06-19 05:15:30 +05:30
#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES )
2002-12-05 04:18:30 +05:30
static FLG_t sav_indx = 0;
static int sav_flgs = -1;
2002-06-19 05:15:30 +05:30
#endif
int i, lwin;
2004-07-07 00:43:38 +05:30
// Display Column Headings -- and distract 'em while we sort (maybe)
PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol);
2002-06-19 05:15:30 +05:30
#ifdef SORT_SUPRESS
if (CHKw(Curwin, NEWFRAM_cwo)
2002-11-30 06:52:01 +05:30
|| sav_indx != q->rc.sortindx
|| sav_flgs != (q->rc.winflags & srtMASK)) {
sav_indx = q->rc.sortindx;
sav_flgs = (q->rc.winflags & srtMASK);
2002-05-30 09:14:46 +05:30
#endif
2002-11-08 06:01:28 +05:30
if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed!
2004-07-07 00:43:38 +05:30
else Frame_srtflg = -1;
2002-11-08 06:01:28 +05:30
Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe
2002-06-19 05:15:30 +05:30
Frame_cmdlin = CHKw(q, Show_CMDLIN);
2002-11-30 06:52:01 +05:30
qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);
2002-06-19 05:15:30 +05:30
#ifdef SORT_SUPRESS
}
#endif
2002-11-08 06:01:28 +05:30
// account for column headings
(*lscr)++;
2002-06-19 05:15:30 +05:30
lwin = 1;
i = 0;
2004-07-07 00:43:38 +05:30
while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) {
if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state))
2002-12-10 08:31:17 +05:30
&& good_uid(ppt[i]) ) {
2004-07-07 00:43:38 +05:30
// Display a process Row
2002-11-08 06:01:28 +05:30
task_show(q, ppt[i]);
(*lscr)++;
2002-06-19 05:15:30 +05:30
++lwin;
2002-05-30 09:14:46 +05:30
}
2002-06-19 05:15:30 +05:30
++i;
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
// for this frame that window's toast, cleanup for next time
2002-06-19 05:15:30 +05:30
q->winlines = 0;
OFFw(Curwin, FLGSOFF_cwo);
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
#ifdef SORT_SUPRESS
#undef srtMASK
#endif
2002-05-30 09:14:46 +05:30
}
2002-11-08 06:01:28 +05:30
/*###### Entry point plus two ##########################################*/
2004-07-07 00:43:38 +05:30
// This guy's just a *Helper* function who apportions the
// remaining amount of screen real estate under multiple windows
2002-11-08 06:01:28 +05:30
static void framehlp (int wix, int max)
2002-05-30 09:14:46 +05:30
{
2002-11-27 05:54:01 +05:30
int i, rsvd, size, wins;
2002-06-19 05:15:30 +05:30
2002-11-08 06:01:28 +05:30
// calc remaining number of visible windows + total 'user' lines
2002-06-19 05:15:30 +05:30
for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
rsvd += Winstk[i].rc.maxtasks;
2002-06-19 05:15:30 +05:30
++wins;
if (max <= rsvd) break;
}
}
if (!wins) wins = 1;
2002-11-08 06:01:28 +05:30
// set aside 'rsvd' & deduct 1 line/window for the columns heading
2002-06-19 05:15:30 +05:30
size = (max - wins) - rsvd;
if (0 <= size) size = max;
size = (max - wins) / wins;
2004-07-07 00:43:38 +05:30
// for remaining windows, set WIN_t winlines to either the user's
// maxtask (1st choice) or our 'foxized' size calculation
// (foxized adj. - 'fair and balanced')
2002-06-19 05:15:30 +05:30
for (i = wix ; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
Winstk[i].winlines =
Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size;
2002-06-19 05:15:30 +05:30
}
}
}
2002-05-30 09:14:46 +05:30
2002-06-19 05:15:30 +05:30
2004-07-07 00:43:38 +05:30
// Initiate the Frame Display Update cycle at someone's whim!
// This routine doesn't do much, mostly he just calls others.
//
// (Whoa, wait a minute, we DO caretake those row guys, plus)
// (we CALCULATE that IMPORTANT Max_lines thingy so that the)
// (*subordinate* functions invoked know WHEN the user's had)
// (ENOUGH already. And at Frame End, it SHOULD be apparent)
// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring)
// (the CURSOR is STUCK in just the RIGHT place, know what I)
// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say)
// (THAT about THIS function again, Ok? Good that's better.)
2002-11-08 06:01:28 +05:30
static void frame_make (void)
2002-06-19 05:15:30 +05:30
{
proc_t **ppt;
int i, scrlins;
2004-07-07 00:43:38 +05:30
// note: all libproc flags are managed by
// reframewins(), who also builds each window's column headers
2002-12-08 04:06:21 +05:30
if (!Frames_libflags) {
2002-11-27 05:54:01 +05:30
reframewins();
memset(Pseudo_scrn, '\0', Pseudo_size);
}
Pseudo_row = Msg_row = scrlins = 0;
2002-11-08 06:01:28 +05:30
ppt = summary_show();
2002-06-19 05:15:30 +05:30
Max_lines = (Screen_rows - Msg_row) - 1;
if (CHKw(Curwin, EQUWINS_cwo))
2002-06-27 05:26:49 +05:30
wins_reflag(Flags_OFF, EQUWINS_cwo);
2002-11-08 06:01:28 +05:30
// sure hope each window's columns header begins with a newline...
2002-06-19 05:15:30 +05:30
putp(tg2(0, Msg_row));
2002-12-05 04:18:30 +05:30
if (!Rc.mode_altscr) {
2002-11-08 06:01:28 +05:30
// only 1 window to show so, piece o' cake
2002-11-30 06:52:01 +05:30
Curwin->winlines = Curwin->rc.maxtasks;
2002-11-08 06:01:28 +05:30
window_show(ppt, Curwin, &scrlins);
2002-06-19 05:15:30 +05:30
} else {
2002-11-08 06:01:28 +05:30
// maybe NO window is visible but assume, pieces o' cakes
2002-06-19 05:15:30 +05:30
for (i = 0 ; i < GROUPSMAX; i++) {
2002-12-05 08:30:42 +05:30
if (CHKw(&Winstk[i], VISIBLE_tsk)) {
2002-11-08 06:01:28 +05:30
framehlp(i, Max_lines - scrlins);
2002-12-05 08:30:42 +05:30
window_show(ppt, &Winstk[i], &scrlins);
2002-06-19 05:15:30 +05:30
}
if (Max_lines <= scrlins) break;
}
2002-05-30 09:14:46 +05:30
}
2004-07-07 00:43:38 +05:30
// clear to end-of-screen (critical if last window is 'idleps off'),
// then put the cursor in-its-place, and rid us of any prior frame's msg
// (main loop must iterate such that we're always called before sleep)
PUTT(
"%s%s%s%s",
scrlins < Max_lines ? "\n" : "",
scrlins < Max_lines ? Cap_clr_eos : "",
tg2(0, Msg_row),
Cap_clr_eol
);
2002-06-19 05:15:30 +05:30
fflush(stdout);
}
2002-05-30 09:14:46 +05:30
2004-07-07 00:43:38 +05:30
int main (int dont_care_argc, char *argv[])
2002-08-26 06:25:30 +05:30
{
2002-10-12 09:55:57 +05:30
(void)dont_care_argc;
2002-08-26 06:25:30 +05:30
before(*argv);
2002-11-08 06:01:28 +05:30
// +-------------+
windows_stage1(); // top (sic) slice
configs_read(); // > spread etc, <
parse_args(&argv[1]); // > lean stuff, <
whack_terminal(); // > onions etc. <
windows_stage2(); // as bottom slice
// +-------------+
signal(SIGALRM, end_pgm);
signal(SIGHUP, end_pgm);
signal(SIGINT, end_pgm);
signal(SIGPIPE, end_pgm);
signal(SIGQUIT, end_pgm);
signal(SIGTERM, end_pgm);
2002-05-30 09:14:46 +05:30
signal(SIGTSTP, suspend);
signal(SIGTTIN, suspend);
signal(SIGTTOU, suspend);
2002-06-19 05:15:30 +05:30
signal(SIGCONT, wins_resize);
signal(SIGWINCH, wins_resize);
2002-05-30 09:14:46 +05:30
2002-10-21 10:20:41 +05:30
for (;;) {
2003-03-18 05:12:00 +05:30
frame_make();
2002-05-30 09:14:46 +05:30
if (Msg_awaiting) show_msg(Msg_delayed);
2004-07-07 00:43:38 +05:30
if (Loops > 0) --Loops;
if (!Loops) end_pgm(0);
2002-05-30 09:14:46 +05:30
2003-03-18 05:12:00 +05:30
tv.tv_sec = Rc.delay_time;
tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
if (Batch) {
select(0, NULL, NULL, NULL, &tv); // ought to loop until done
} else {
long file_flags;
2005-01-06 06:19:09 +05:30
int rc;
2003-03-18 05:12:00 +05:30
char c;
fd_set fs;
2002-05-30 09:14:46 +05:30
FD_ZERO(&fs);
FD_SET(STDIN_FILENO, &fs);
2003-03-18 05:12:00 +05:30
file_flags = fcntl(STDIN_FILENO, F_GETFL);
if(file_flags==-1) file_flags=0;
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
2005-01-06 06:19:09 +05:30
2003-03-18 05:12:00 +05:30
// check 1st, in case tv zeroed (by sig handler) before it got set
2005-01-06 06:19:09 +05:30
rc = chin(0, &c, 1);
if (rc <= 0) {
// EOF is pretty much a "can't happen" except for a kernel bug.
// We should quickly die via SIGHUP, and thus not spin here.
// if (rc == 0) end_pgm(0); /* EOF from terminal */
fcntl(STDIN_FILENO, F_SETFL, file_flags);
select(1, &fs, NULL, NULL, &tv);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
}
if (chin(0, &c, 1) > 0) {
fcntl(STDIN_FILENO, F_SETFL, file_flags);
do_key((unsigned)c);
} else {
fcntl(STDIN_FILENO, F_SETFL, file_flags);
}
2002-05-30 09:14:46 +05:30
}
2002-10-21 10:20:41 +05:30
}
2002-05-30 09:14:46 +05:30
return 0;
2002-02-02 04:17:29 +05:30
}