--- /dev/null
+/*
+ * top.c - show top CPU processes
+ *
+ * Copyright (c) 1992 Branko Lankester
+ * Copyright (c) 1992 Roger Binns
+ * Copyright (c) 1997 Michael K. Johnson
+ *
+ * Snarfed and HEAVILY modified in december 1992 for procps
+ * by Michael K. Johnson, johnsonm@sunsite.unc.edu.
+ *
+ * Modified Michael K. Johnson's ps to make it a top program.
+ * Also borrowed elements of Roger Binns kmem based top program.
+ * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com)
+ * 1/93
+ *
+ * Modified by Michael K. Johnson to be more efficient in cpu use
+ * 2/21/93
+ *
+ * Changed top line to use uptime for the load average. Also
+ * added SIGTSTP handling. J. Cowley, 19 Mar 1993.
+ *
+ * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu)
+ * 1994/04/02. Secure mode added. "d" option added. Argument parsing
+ * improved. Switched order of tick display to user, system, nice, idle,
+ * because it makes more sense that way. Style regularized (to K&R,
+ * more or less). Cleaned up much throughout. Added cumulative mode.
+ * Help screen improved.
+ *
+ * Fixed kill buglet brought to my attention by Rob Hooft.
+ * Problem was mixing of stdio and read()/write(). Added
+ * getnum() to solve problem.
+ * 12/30/93 Michael K. Johnson
+ *
+ * Added toggling output of idle processes via 'i' key.
+ * 3/29/94 Gregory K. Nickonov
+ *
+ * Fixed buglet where rawmode wasn't getting restored.
+ * Added defaults for signal to send and nice value to use.
+ * 5/4/94 Jon Tombs.
+ *
+ * Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
+ * Merged previous changes to 0.8 into 0.95.
+ * Allowed the use of symbolic names (e.g., "HUP") for signal input.
+ * Rewrote getnum() into getstr(), getint(), getsig(), etc.
+ *
+ * Modified 1995 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+ * added kmem top functionality (configurable fields)
+ * configurable order of process display
+ * Added options for dis/enabling uptime, statistics, and memory info.
+ * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields)
+ *
+ * Modified 1996/05/18 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+ * Use of new interface and general cleanup. The code should be far more
+ * readable than before.
+ *
+ * Modified 1996/06/25 Zygo Blaxell <zblaxell@ultratech.net>
+ * Added field scaling code for programs that run more than two hours or
+ * take up more than 100 megs. We have lots of both on our production line.
+ *
+ * Modified 1998/02/21 Kirk Bauer <kirk@kaybee.org>
+ * Added the 'u' option to display only a selected user... plus it will
+ * take into account that not all 20 top processes are actually shown,
+ * so it can fit more onto the screen. I think this may help the
+ * 'don't show idle' mode, but I'm not sure.
+ *
+ * Modified 1997/07/27 & 1999/01/27 Tim Janik <timj@gtk.org>
+ * added `-p' option to display specific process ids.
+ * process sorting is by default disabled in this case.
+ * added `N' and `A' keys to sort the tasks Numerically by pid or
+ * sort them by Age (newest first).
+ *
+ * Modified 1999/10/22 Tim Janik <timj@gtk.org>
+ * miscellaneous minor fixes, including "usage: ..." output for
+ * unrecognized options.
+ *
+ * Modified 2000/02/07 Jakub Jelinek <jakub@redhat.com>
+ * Only load System.map when we are going to display WCHAN.
+ * Show possible error messages from that load using SHOWMESSAGE.
+ *
+ * Modified 2000/07/10 Michael K. Johnson <johnsonm@redhat.com>
+ * Integrated a patch to display SMP information.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+#include <termcap.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <sys/param.h>
+#include <locale.h>
+
+#include "proc/sysinfo.h"
+#include "proc/procps.h"
+#include "proc/whattime.h"
+#include "proc/sig.h"
+#include "proc/version.h"
+#include "proc/readproc.h"
+#include "proc/status.h"
+#include "proc/devname.h"
+#include "proc/compare.h"
+
+#define PUTP(x) (tputs(x,1,putchar))
+#define BAD_INPUT -30
+
+#include "top.h" /* new header for top specific things */
+
+static int *cpu_mapping;
+static int nr_cpu;
+
+/*#######################################################################
+ *#### Startup routines: parse_options, get_options, ##############
+ *#### setup_terminal and main ##############
+ *#######################################################################
+ */
+
+ /*
+ * parse the options string as read from the config file(s).
+ * if top is in secure mode, disallow changing of the delay time between
+ * screen updates.
+ */
+static void parse_options(char *Options, int secure)
+{
+ int i;
+ for (i = 0; i < strlen(Options); i++) {
+ switch (Options[i]) {
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!secure)
+ Sleeptime = (float) Options[i] - '0';
+ break;
+ case 'S':
+ Cumulative = 1;
+ headers[22][1] = 'C';
+ break;
+ case 's':
+ Secure = 1;
+ break;
+ case 'i':
+ Noidle = 1;
+ break;
+ case 'm':
+ show_memory = 0;
+ header_lines -= 2;
+ break;
+ case 'M':
+ sort_type = S_MEM;
+ reset_sort_options();
+ register_sort_function( -1, (cmp_t)mem_sort);
+ break;
+ case 'l':
+ show_loadav = 0;
+ header_lines -= 1;
+ break;
+ case 'P':
+ sort_type = S_PCPU;
+ reset_sort_options();
+ register_sort_function( -1, (cmp_t)pcpu_sort);
+ break;
+ case 'N':
+ sort_type = S_NONE;
+ reset_sort_options();
+ break;
+ case 'A':
+ sort_type = S_AGE;
+ reset_sort_options();
+ register_sort_function( -1, (cmp_t)age_sort);
+ break;
+ case 't':
+ show_stats = 0;
+ header_lines -= 2;
+ break;
+ case 'T':
+ sort_type = S_TIME;
+ reset_sort_options();
+ register_sort_function( -1, (cmp_t)time_sort);
+ break;
+ case 'c':
+ show_cmd = 0;
+ break;
+ case '\n':
+ break;
+ case 'I':
+ Irixmode = 0;
+ break;
+ default:
+ fprintf(stderr, "Wrong configuration option %c\n", i);
+ exit(1);
+ break;
+ }
+ }
+}
+
+/*
+ * Read the configuration file(s). There are two files, once SYS_TOPRC
+ * which should only contain the secure switch and a sleeptime
+ * value iff ordinary users are to use top in secure mode only.
+ *
+ * The other file is $HOME/RCFILE.
+ * The configuration file should contain two lines (any of which may be
+ * empty). The first line specifies the fields that are to be displayed
+ * in the order you want them to. Uppercase letters specify fields
+ * displayed by default, lowercase letters specify fields not shown by
+ * default. The order of the letters in this line corresponds to the
+ * order of the displayed fileds.
+ *
+ * all Options but 'q' can be read from this config file
+ * The delay time option syntax differs from the commandline syntax:
+ * only integer values between 2 and 9 seconds are recognized
+ * (this is for standard configuration, so I think this should do).
+ *
+ * usually this file is not edited by hand, but written from top using
+ * the 'W' command.
+ */
+
+static void get_options(void)
+{
+ FILE *fp;
+ char *pt;
+ char *rcfile = NULL; /* path to rc file... */
+ char *home = NULL; /* path of user's home directory... */
+ size_t home_length = 0; /* length of path... */
+ char Options[256] = "";
+ int i;
+
+ nr_cpu = sysconf (_SC_NPROCESSORS_ONLN);
+ if (nr_cpu < 1) nr_cpu = 1;
+ cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu);
+ /* read cpuname */
+ for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i;
+ header_lines = 6 + nr_cpu;
+ fp = fopen(SYS_TOPRC, "r");
+ if (fp != NULL) {
+ fgets(Options, 254, fp);
+ fclose(fp);
+ }
+ parse_options(Options, 0);
+ strcpy(Options, "");
+
+ if ( (home = getenv("HOME")) != NULL) {
+ home_length = strlen(home);
+ }
+
+ if ( (rcfile = malloc(home_length + strlen(RCFILE) + 2))) {
+ if (home != NULL) {
+ strcpy(rcfile, home);
+ strcat(rcfile, "/");
+ }
+ strcat(rcfile, RCFILE);
+ fp = fopen(rcfile, "r");
+ if (fp == NULL) {
+ strcpy(Fields, DEFAULT_SHOW);
+ } else {
+ if (fgets(Fields, 254, fp) != NULL) {
+ pt = strchr(Fields, '\n');
+ if (pt) *pt = 0;
+ }
+ fgets(Options, 254, fp);
+ fclose(fp);
+ }
+
+ free(rcfile);
+ }
+ parse_options(Options, getuid()? Secure : 0);
+}
+
+/*
+ * Set up the terminal attributes.
+ */
+static void setup_terminal(void)
+{
+ char *termtype;
+ struct termios newtty;
+ if (!Batch)
+ termtype = getenv("TERM");
+ else
+ termtype = "dumb";
+ if (!termtype) {
+ /* In theory, $TERM should never not be set, but in practice,
+ some gettys don't. Fortunately, vt100 is nearly always
+ correct (or pretty close). */
+ termtype = "VT100";
+ /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */
+ /* exit(1); */
+ }
+
+ /*
+ * Get termcap entries and window size.
+ */
+ if(tgetent(NULL, termtype) != 1) {
+ fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n",
+ termtype);
+ exit(1);
+ }
+
+ cm = tgetstr("cm", 0);
+ top_clrtobot = tgetstr("cd", 0);
+ cl = tgetstr("cl", 0);
+ top_clrtoeol = tgetstr("ce", 0);
+ ho = tgetstr("ho", 0);
+ md = tgetstr("md", 0);
+ mr = tgetstr("mr", 0);
+ me = tgetstr("me", 0);
+
+
+ if (Batch) return; /* the rest doesn't apply to batch mode */
+ if (tcgetattr(0, &Savetty) == -1) {
+ perror(PROGNAME ": tcgetattr() failed");
+ error_end(errno);
+ }
+ newtty = Savetty;
+ newtty.c_lflag &= ~ICANON;
+ newtty.c_lflag &= ~ECHO;
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+ if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) {
+ printf("cannot put tty into raw mode\n");
+ error_end(1);
+ }
+ tcgetattr(0, &Rawtty);
+}
+
+static int parseint(const char *src, const char *err)
+{
+ char *endp;
+ int num;
+ int len;
+ num = strtol(src, &endp, 0);
+ if (*endp == '\0') return num;
+ /* also accept prefixes of: infinite, infinity, maximum, all */
+ len = strlen(src);
+ if(len<1) goto fail;
+ if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX;
+ if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX;
+ if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX;
+ if(len<4 && !strncmp(src,"all" ,len)) return INT_MAX;
+fail:
+ fprintf(stderr, err, src);
+ exit(1);
+}
+
+static double parseflt(const char *src, const char *err)
+{
+ char *endp;
+ double num;
+ int len;
+ num = strtod(src, &endp);
+ if (*endp == '\0') return num;
+ /* also accept prefixes of: infinite, infinity, maximum, all */
+ len = strlen(src);
+ if(len<1) goto fail;
+ if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX;
+ if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX;
+ if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX;
+ if(len<4 && !strncmp(src,"all" ,len)) return (double)INT_MAX;
+fail:
+ fprintf(stderr, err, src);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ /* For select(2). */
+ struct timeval tv;
+ fd_set in;
+ /* For parsing arguments. */
+ char *cp;
+ /* The key read in. */
+ char c;
+
+ struct sigaction sact;
+
+ setlocale(LC_ALL, "");
+ get_options();
+
+ /* set to PCPU sorting */
+ register_sort_function( -1, (cmp_t)pcpu_sort);
+
+ /*
+ * Parse arguments.
+ */
+ (void)argc;
+ argv++;
+ while (*argv) {
+ cp = *argv++;
+ while (*cp) {
+ switch (*cp) {
+ case 'd':
+ if (cp[1]) {
+ Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n");
+ goto breakargv;
+ } else if (*argv) { /* last char in an argv, use next as arg */
+ Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n");
+ goto breakargv;
+ } else {
+ fprintf(stderr, "-d requires an argument\n");
+ exit(1);
+ }
+ break;
+ case 'n':
+ if (cp[1]) {
+ Loops = parseint(++cp, PROGNAME ": Bad value %s'\n");
+ goto breakargv;
+ } else if (*argv) { /* last char in an argv, use next as arg */
+ Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n");
+ goto breakargv;
+ }
+ break;
+
+ case 'q':
+ if (!getuid())
+ /* set priority to -10 in order to stay above kswapd */
+ if (setpriority(PRIO_PROCESS, getpid(), -10)) {
+ /* We check this just for paranoia. It's not
+ fatal, and shouldn't happen. */
+ perror(PROGNAME ": setpriority() failed");
+ }
+ Sleeptime = 0;
+ break;
+ case 'p':
+ if (monpids_index >= monpids_max) {
+ fprintf(stderr, PROGNAME ": More than %u process ids specified\n",
+ monpids_max);
+ exit(1);
+ }
+ if (cp[1]) {
+ if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 ||
+ monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+ fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+ exit(1);
+ }
+ } else if (*argv) { /* last char in an argv, use next as arg */
+ if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 ||
+ monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+ fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "-p requires an argument\n");
+ exit(1);
+ }
+ if (!monpids[monpids_index])
+ monpids[monpids_index] = getpid();
+ /* default to no sorting when monitoring process ids */
+ if (!monpids_index++) {
+ sort_type = S_NONE;
+ reset_sort_options();
+ }
+ cp = "_";
+ break;
+ case 'b':
+ Batch = 1;
+ break;
+ case 'c':
+ show_cmd = !show_cmd;
+ break;
+ case 'S':
+ Cumulative = 1;
+ break;
+ case 'i':
+ Noidle = 1;
+ break;
+ case 's':
+ Secure = 1;
+ break;
+ case 'C':
+ CPU_states = 1;
+ break;
+ case '-':
+ break; /* Just ignore it */
+ case 'v':
+ case 'V':
+ fprintf(stdout, "top (%s)\n", procps_version);
+ exit(0);
+ case 'h':
+ fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+ exit(0);
+ default:
+ fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp);
+ fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+ exit(1);
+ }
+ cp++;
+ }
+ breakargv:
+ }
+
+ if (nr_cpu > 1 && CPU_states)
+ header_lines++;
+
+ meminfo(); /* need kb_main_total value filled in */
+
+ setup_terminal();
+ window_size(0);
+ /*
+ * Set up signal handlers.
+ */
+ sact.sa_handler = end;
+ sact.sa_flags = 0;
+ sigemptyset(&sact.sa_mask);
+ sigaction(SIGHUP, &sact, NULL);
+ sigaction(SIGINT, &sact, NULL);
+ sigaction(SIGQUIT, &sact, NULL);
+ sact.sa_handler = stop;
+ sact.sa_flags = SA_RESTART;
+ sigaction(SIGTSTP, &sact, NULL);
+ sact.sa_handler = window_size;
+ sigaction(SIGWINCH, &sact, NULL);
+ sigaction(SIGCONT, &sact, NULL);
+
+ /* loop, collecting process info and sleeping */
+ while (1) {
+ if (Loops > 0)
+ Loops--;
+ /* display the tasks */
+ show_procs();
+ /* sleep & wait for keyboard input */
+ if (Loops == 0)
+ end(0);
+ if (!Batch)
+ {
+ tv.tv_sec = Sleeptime;
+ tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000;
+ FD_ZERO(&in);
+ FD_SET(0, &in);
+ if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1)
+ do_key(c);
+ } else {
+ sleep(Sleeptime);
+ }
+ }
+}
+
+/*#######################################################################
+ *#### Signal handled routines: error_end, end, stop, window_size ###
+ *#### Small utilities: make_header, getstr, getint, getfloat, getsig ###
+ *#######################################################################
+ */
+
+
+ /*
+ * end when exiting with an error.
+ */
+static void error_end(int rno)
+{
+ if (!Batch)
+ tcsetattr(0, TCSAFLUSH, &Savetty);
+ PUTP(tgoto(cm, 0, Lines - 1));
+ fputs("\r\n", stdout);
+ exit(rno);
+}
+/*
+ * Normal end of execution.
+ */
+static void end(int signo)
+{
+ (void)signo;
+ if (!Batch)
+ tcsetattr(0, TCSAFLUSH, &Savetty);
+ PUTP(tgoto(cm, 0, Lines - 1));
+ fputs("\r\n", stdout);
+ exit(0);
+}
+
+/*
+ * SIGTSTP catcher.
+ */
+static void stop(int signo)
+{
+ (void)signo;
+ /* Reset terminal. */
+ if (!Batch)
+ tcsetattr(0, TCSAFLUSH, &Savetty);
+ PUTP(tgoto(cm, 0, Lines - 3));
+ fflush(stdout);
+ raise(SIGSTOP);
+ /* Later... */
+ if (!Batch)
+ tcsetattr (0, TCSAFLUSH, &Rawtty);
+}
+
+/*
+ * Reads the window size and clear the window. This is called on setup,
+ * and also catches SIGWINCHs, and adjusts Maxlines. Basically, this is
+ * the central place for window size stuff.
+ */
+static void window_size(int signo)
+{
+ struct winsize ws;
+ (void)signo;
+ if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){
+ Cols = ws.ws_col;
+ Lines = ws.ws_row;
+ }else{
+ Cols = tgetnum("co");
+ Lines = tgetnum("li");
+ }
+ if (!Batch)
+ clear_screen();
+ /*
+ * calculate header size, length of cmdline field ...
+ */
+ Numfields = make_header();
+}
+
+/*
+ * this prints a possible message from open_psdb_message
+ */
+static void top_message(const char *format, ...) {
+ va_list arg;
+ int n;
+ char buffer[512];
+
+ va_start (arg, format);
+ n = vsnprintf (buffer, 512, format, arg);
+ va_end (arg);
+ if (n > -1 && n < 512)
+ SHOWMESSAGE(("%s", buffer));
+}
+
+/*
+ * this adjusts the lines needed for the header to the current value
+ */
+static int make_header(void)
+{
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < strlen(Fields); i++) {
+ if (Fields[i] < 'a') {
+ pflags[j++] = Fields[i] - 'A';
+ if (Fields[i] == 'U' && CL_wchan_nout == -1) {
+ CL_wchan_nout = 0;
+ /* for correct handling of WCHAN fields, we have to do distingu
+ * between kernel versions */
+ /* get kernel symbol table, if needed */
+ if (open_psdb_message(NULL, top_message)) {
+ CL_wchan_nout = 1;
+ } else {
+ psdbsucc = 1;
+ }
+ }
+ }
+ }
+ strcpy(Header, "");
+ for (i = 0; i < j; i++)
+ strcat(Header, headers[pflags[i]]);
+ /* readjust window size ... */
+ Maxcmd = Cols - strlen(Header) + 7;
+ Maxlines = Display_procs ? Display_procs : Lines - header_lines;
+ if (Maxlines > Lines - header_lines)
+ Maxlines = Lines - header_lines;
+ return (j);
+}
+
+
+
+/*
+ * Get a string from the user; the base of getint(), et al. This really
+ * ought to handle long input lines and errors better. NB: The pointer
+ * returned is a statically allocated buffer, so don't expect it to
+ * persist between calls.
+ */
+static char *getstr(void)
+{
+ static char line[BUFSIZ]; /* BUFSIZ from <stdio.h>; arbitrary */
+ int i = 0;
+
+ /* Must make sure that buffered IO doesn't kill us. */
+ fflush(stdout);
+ fflush(stdin); /* Not POSIX but ok */
+
+ do {
+ read(STDIN_FILENO, &line[i], 1);
+ } while (line[i++] != '\n' && i < sizeof(line));
+ line[--i] = 0;
+
+ return (line);
+}
+
+
+/*
+ * Get an integer from the user. Display an error message and
+ * return BAD_INPUT if it's invalid; else return the number.
+ */
+static int getint(void)
+{
+ char *line;
+ int i;
+ int r;
+
+ line = getstr();
+
+ for (i = 0; line[i]; i++) {
+ if (!isdigit(line[i]) && line[i] != '-') {
+ SHOWMESSAGE(("That's not a number!"));
+ return (BAD_INPUT);
+ }
+ }
+
+ /* An empty line is a legal error (hah!). */
+ if (!line[0])
+ return (BAD_INPUT);
+
+ sscanf(line, "%d", &r);
+ return (r);
+}
+
+
+/*
+ * Get a float from the user. Just like getint().
+ */
+static float getfloat(void)
+{
+ char *line;
+ int i;
+ float r;
+ char *savelocale;
+
+ line = getstr();
+
+ for (i = 0; line[i]; i++) {
+ if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') {
+ SHOWMESSAGE(("That's not a float!"));
+ return (BAD_INPUT);
+ }
+ }
+
+ /* An empty line is a legal error (hah!). */
+ if (!line[0])
+ return (BAD_INPUT);
+
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ sscanf(line, "%f", &r);
+ setlocale(LC_NUMERIC, savelocale);
+ return (r);
+}
+
+
+/*
+ * Get a signal number or name from the user. Return the number, or -1
+ * on error.
+ */
+static int getsig(void)
+{
+ char *line;
+
+ /* This is easy. */
+ line = getstr();
+ return signal_name_to_number(line);
+}
+
+/*#######################################################################
+ *#### Routine for sorting on used time, resident memory and %CPU #####
+ *#### It would be easy to include full sorting capability as in #####
+ *#### ps, but I think there is no real use for something that #####
+ *#### complicated. Using register_sort_function or parse_sort_opt #####
+ *#### you just have to do the natural thing and it will work. #####
+ *#######################################################################
+ */
+
+static int time_sort (proc_t **P, proc_t **Q)
+{
+ if (Cumulative) {
+ if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) <
+ ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return -1;
+ if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) >
+ ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return 1;
+ } else {
+ if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+ return -1;
+ if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+ return 1;
+ }
+ return 0;
+}
+
+static int pcpu_sort (proc_t **P, proc_t **Q)
+{
+ if( (*P)->pcpu < (*Q)->pcpu ) return -1;
+ if( (*P)->pcpu > (*Q)->pcpu ) return 1;
+ return 0;
+}
+
+static int mem_sort (proc_t **P, proc_t **Q)
+{
+ if( (*P)->vm_rss < (*Q)->vm_rss ) return -1;
+ if( (*P)->vm_rss > (*Q)->vm_rss ) return 1;
+ return 0;
+}
+
+int age_sort (proc_t **P, proc_t **Q)
+{
+ if( (*P)->start_time < (*Q)->start_time ) return -1;
+ if( (*P)->start_time > (*Q)->start_time ) return 1;
+ return 0;
+}
+
+/*#######################################################################
+ *#### Routines handling the field selection/ordering screens: ########
+ *#### show_fields, change_order, change_fields ########
+ *#######################################################################
+ */
+
+ /*
+ * Display the specification line of all fields. Upper case indicates
+ * a displayed field, display order is according to the order of the
+ * letters. A short description of each field is shown as well.
+ * The description of a displayed field is marked by a leading
+ * asterisk (*).
+ */
+static void show_fields(void)
+{
+ int i, row, col;
+ char *p;
+
+ clear_screen();
+ PUTP(tgoto(cm, 3, 0));
+ printf("Current Field Order: %s\n", Fields);
+ for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) {
+ row = i % (Lines - 3) + 3;
+ col = i / (Lines - 3) * 40;
+ PUTP(tgoto(cm, col, row));
+ for (p = headers[i]; *p == ' '; ++p);
+ printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A',
+ p, headers2[i]);
+ }
+}
+
+/*
+ * change order of displayed fields
+ */
+static void change_order(void)
+{
+ char c, ch, *p;
+ int i;
+
+ show_fields();
+ for (;;) {
+ PUTP(tgoto(cm, 0, 0));
+ PUTP(top_clrtoeol);
+ PUTP(tgoto(cm, 3, 0));
+ PUTP(mr);
+ printf("Current Field Order: %s", Fields);
+ PUTP(me);
+ putchar('\n');
+ PUTP(tgoto(cm, 0, 1));
+ printf("Upper case characters move a field to the left, lower case to the right");
+ fflush(stdout);
+ if (!Batch) { /* should always be true, but... */
+ tcsetattr(0, TCSAFLUSH, &Rawtty);
+ read(0, &c, 1);
+ tcsetattr(0, TCSAFLUSH, &Savetty);
+ }
+ i = toupper(c) - 'A';
+ if ((p = strchr(Fields, i + 'A')) != NULL) {
+ if (isupper(c))
+ p--;
+ if ((p[1] != '\0') && (p >= Fields)) {
+ ch = p[0];
+ p[0] = p[1];
+ p[1] = ch;
+ }
+ } else if ((p = strchr(Fields, i + 'a')) != NULL) {
+ if (isupper(c))
+ p--;
+ if ((p[1] != '\0') && (p >= Fields)) {
+ ch = p[0];
+ p[0] = p[1];
+ p[1] = ch;
+ }
+ } else {
+ break;
+ }
+ }
+ Numfields = make_header();
+}
+/*
+ * toggle displayed fields
+ */
+static void change_fields(void)
+{
+ int i, changed = 0;
+ int row, col;
+ char c, *p;
+ char tmp[2] = " ";
+
+ show_fields();
+ for (;;) {
+ PUTP(tgoto(cm, 0, 0));
+ PUTP(top_clrtoeol);
+ PUTP(tgoto(cm, 3, 0));
+ PUTP(mr);
+ printf("Current Field Order: %s", Fields);
+ PUTP(me);
+ putchar('\n');
+ PUTP(tgoto(cm, 0, 1));
+ if (!Batch) { /* should always be true, but... */
+ printf("Toggle fields with a-x, any other key to return: ");
+ fflush(stdout);
+ tcsetattr(0, TCSAFLUSH, &Rawtty);
+ read(0, &c, 1);
+ tcsetattr(0, TCSAFLUSH, &Savetty);
+ }
+ i = toupper(c) - 'A';
+ if (i >= 0 && i < sizeof headers / sizeof headers[0]) {
+ row = i % (Lines - 3) + 3;
+ col = i / (Lines - 3) * 40;
+ PUTP(tgoto(cm, col, row));
+ if ((p = strchr(Fields, i + 'A')) != NULL) { /* deselect Field */
+ *p = i + 'a';
+ putchar(' ');
+ } else if ((p = strchr(Fields, i + 'a')) != NULL) { /* select previously */
+ *p = i + 'A'; /* deselected field */
+ putchar('*');
+ } else { /* select new field */
+ tmp[0] = i + 'A';
+ strcat(Fields, tmp);
+ putchar('*');
+ }
+ changed = 1;
+ fflush(stdout);
+ } else
+ break;
+ }
+ if (changed)
+ Numfields = make_header();
+}
+
+/* Do the scaling stuff: interprets time in seconds, formats it to
+ * fit width, and returns pointer to static char*.
+ */
+static char *scale_time(int t,int width)
+{
+ static char buf[100];
+
+ /* Try successively higher units until it fits */
+
+ sprintf(buf,"%d:%02d",t/60,t%60); /* minutes:seconds */
+ if (strlen(buf)<=width)
+ return buf;
+
+ t/=60; /* minutes */
+ sprintf(buf,"%dm",t);
+ if (strlen(buf)<=width)
+ return buf;
+
+ t/=60; /* hours */
+ sprintf(buf,"%dh",t);
+ if (strlen(buf)<=width)
+ return buf;
+
+ t/=24; /* days */
+ sprintf(buf,"%dd",t);
+ if (strlen(buf)<=width)
+ return buf;
+
+ t/=7; /* weeks */
+ sprintf(buf,"%dw",t);
+ return buf; /* this is our last try;
+ if it still doesn't fit, too bad. */
+
+ /* FIXME: if someone has a 16-way SMP running over a year... */
+}
+
+/* scale_k(k,width,unit) - interprets k as a count, formats to fit width.
+ if unit is 0, k is a byte count; 1 is a kilobyte
+ count; 2 for megabytes; 3 for gigabytes.
+*/
+
+static char *scale_k(int k,int width,int unit)
+{
+ /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
+ static double scale[]={1024,1024*1024,1024*1024*1024,0};
+ /* kilo, mega, giga, tera */
+ static char unitletters[]={'K','M','G','T',0};
+ static char buf[100];
+ char *up;
+ double *dp;
+
+ /* Try successively higher units until it fits */
+
+ sprintf(buf,"%d",k);
+ if (strlen(buf)<=width)
+ return buf;
+
+ for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) {
+ sprintf(buf,"%.1f%c",k / *dp,*up);
+ if (strlen(buf)<=width)
+ return buf;
+ sprintf(buf,"%d%c",(int)(k / *dp),*up);
+ if (strlen(buf)<=width)
+ return buf;
+ }
+
+ /* Give up; give them what we got on our shortest attempt */
+ return buf;
+}
+
+/*
+ *#######################################################################
+ *#### Routines handling the main top screen: ########
+ *#### show_task_info, show_procs, show_memory, do_stats ########
+ *#######################################################################
+ */
+ /*
+ * Displays infos for a single task
+ */
+static void show_task_info(proc_t *task)
+{
+ int i,j;
+ unsigned int t;
+ char *cmdptr;
+ char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p;
+
+ for (i = 0; i < Numfields; i++) {
+ tmp[0] = 0;
+ switch (pflags[i]) {
+ case P_PID:
+ sprintf(tmp, "%5d ", task->pid);
+ break;
+ case P_PPID:
+ sprintf(tmp, "%5d ", task->ppid);
+ break;
+ case P_EUID:
+ sprintf(tmp, "%4d ", task->euid);
+ break;
+ case P_EUSER:
+ sprintf(tmp, "%-8.8s ", task->euser);
+ break;
+ case P_PCPU:
+ sprintf(tmp, "%4.1f ", (float)task->pcpu / 10);
+ break;
+ case P_LCPU:
+ sprintf(tmp, "%2d ", task->processor);
+ break;
+ case P_PMEM: {
+ unsigned pmem;
+ pmem = task->vm_rss * 1000ULL / kb_main_total;
+ if (pmem > 999) pmem = 999;
+ sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U);
+ }
+ break;
+ case P_TTY: {
+ char outbuf[9];
+ dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV);
+ sprintf(tmp, "%-8.8s ", outbuf);
+ }
+ break;
+ case P_PRI:
+ sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0));
+ break;
+ case P_NICE:
+ sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0));
+ break;
+ case P_PAGEIN:
+ sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0));
+ break;
+ case P_TSIZ:
+ sprintf(tmp, "%5.5s ",
+ scale_k(((task->end_code - task->start_code) / 1024), 5, 1));
+ break;
+ case P_DSIZ:
+ sprintf(tmp, "%5.5s ",
+ scale_k(((task->vsize - task->end_code) / 1024), 5, 1));
+ break;
+ case P_SIZE:
+ sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1));
+ break;
+ case P_TRS:
+ sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1));
+ break;
+ case P_SWAP:
+ sprintf(tmp, "%4.4s ",
+ scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1));
+ break;
+ case P_SHARE:
+ sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1));
+ break;
+ case P_A:
+ sprintf(tmp, "%3.3s ", "NYI");
+ break;
+ case P_WP:
+ sprintf(tmp, "%3.3s ", "NYI");
+ break;
+ case P_DT:
+ sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0));
+ break;
+ case P_RSS: /* rss, not resident (which includes IO memory) */
+ sprintf(tmp, "%4.4s ",
+ scale_k((task->rss << CL_pg_shift), 4, 1));
+ break;
+ case P_WCHAN:
+ if (!CL_wchan_nout)
+ sprintf(tmp, "%-9.9s ", wchan(task->wchan));
+ else
+ sprintf(tmp, "%-9lx", task->wchan);
+ break;
+ case P_STAT:
+ sprintf(tmp, "%-4.4s ", status(task));
+ break;
+ case P_TIME:
+ t = (task->utime + task->stime) / Hertz;
+ if (Cumulative)
+ t += (task->cutime + task->cstime) / Hertz;
+ sprintf(tmp, "%6.6s ", scale_time(t,6));
+ break;
+ case P_COMMAND:
+ if (!show_cmd && task->cmdline && *(task->cmdline)) {
+ j=0;
+ while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){
+/* #if 0 */ /* This is useless? FIXME */
+ if (j > 0)
+ strcat(tmp3, " ");
+/* #endif */
+ strncat(tmp3, (task->cmdline)[j], 1000);
+ j++;
+ }
+ cmdptr = tmp3;
+ } else {
+ cmdptr = task->cmd;
+ }
+ if (strlen(cmdptr) > Maxcmd)
+ cmdptr[Maxcmd - 1] = 0;
+ sprintf(tmp, "%s", cmdptr);
+ tmp3[0]=0;
+ break;
+ case P_FLAGS:
+ sprintf(tmp, "%8lx ", task->flags);
+ break;
+ }
+ strcat(tmp2, tmp);
+ }
+ if (strlen(tmp2) > Cols - 1)
+ tmp2[Cols - 1] = 0;
+
+ /* take care of cases like:
+ perl -e 'foo
+ bar foo bar
+ foo
+ # end of perl script'
+ */
+ for (p=tmp2;*p;++p)
+ if (!isgraph(*p))
+ *p=' ';
+
+ printf("\n%s", tmp2);
+ PUTP(top_clrtoeol);
+}
+
+/*
+ * This is the real program! Read process info and display it.
+ * One could differentiate options of readproctable2, perhaps it
+ * would be useful to support the PROC_UID and PROC_TTY
+ * as command line options.
+ */
+static void show_procs(void)
+{
+ static proc_t **p_table=NULL;
+ static int proc_flags;
+ int count;
+ int ActualLines;
+ float elapsed_time;
+ static int first=0;
+
+ if (first==0) {
+ proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT;
+ if (monpids_index)
+ proc_flags |= PROC_PID;
+ p_table=readproctab2(proc_flags, p_table, monpids);
+ elapsed_time = get_elapsed_time();
+ do_stats(p_table, elapsed_time, 0);
+ sleep(1);
+ first=1;
+ }
+ if (first && Batch)
+ fputs("\n\n",stdout);
+ /* Display the load averages. */
+ PUTP(ho);
+ PUTP(md);
+ if (show_loadav) {
+ printf("%s", sprint_uptime());
+ PUTP(top_clrtoeol);
+ putchar('\n');
+ }
+ p_table=readproctab2(proc_flags, p_table, monpids);
+ /* Immediately find out the elapsed time for the frame. */
+ elapsed_time = get_elapsed_time();
+ /* Display the system stats, calculate percent CPU time
+ * and sort the list. */
+ do_stats(p_table, elapsed_time,1);
+ /* Display the memory and swap space usage. */
+ show_meminfo();
+ if (strlen(Header) + 2 > Cols)
+ Header[Cols - 2] = 0;
+ PUTP(mr);
+ fputs(Header, stdout);
+ PUTP(top_clrtoeol);
+ PUTP(me);
+
+ /*
+ * Finally! Loop through to find the top task, and display it.
+ * Lather, rinse, repeat.
+ */
+ count = 0;
+ ActualLines = 0;
+ while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) {
+ char Stat;
+
+ Stat = p_table[count]->state;
+
+ if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) &&
+ ( (CurrUser[0] == '\0') ||
+ (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
+
+ /*
+ * Show task info.
+ */
+ show_task_info(p_table[count]);
+ if (!Batch)
+ ActualLines++;
+ }
+ count++;
+ }
+ PUTP(top_clrtobot);
+ PUTP(tgoto(cm, 0, header_lines - 2));
+ fflush(stdout);
+}
+
+
+/*
+ * Finds the current time (in microseconds) and calculates the time
+ * elapsed since the last update. This is essential for computing
+ * percent CPU usage.
+ */
+static float get_elapsed_time(void)
+{
+ struct timeval t;
+ static struct timeval oldtime;
+ struct timezone timez;
+ float elapsed_time;
+
+ gettimeofday(&t, &timez);
+ elapsed_time = (t.tv_sec - oldtime.tv_sec)
+ + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
+ oldtime.tv_sec = t.tv_sec;
+ oldtime.tv_usec = t.tv_usec;
+ return (elapsed_time);
+}
+
+/*
+ * Reads the memory info and displays it. Returns the total memory
+ * available, for use in percent memory usage calculations.
+ */
+static void show_meminfo(void)
+{
+ meminfo(); /* read+parse /proc/meminfo */
+ if (show_memory) {
+ printf(
+ "Mem: %8dK total, %8dK used, %8dK free, %8dK buffers",
+ kb_main_total,
+ kb_main_used,
+ kb_main_free,
+ kb_main_buffers
+ );
+ PUTP(top_clrtoeol);
+ putchar('\n');
+ printf(
+ "Swap: %8dK total, %8dK used, %8dK free, %8dK cached",
+ kb_swap_total,
+ kb_swap_used,
+ kb_swap_free,
+ kb_main_cached
+ );
+ PUTP(top_clrtoeol);
+ putchar('\n');
+ }
+ PUTP(me);
+ PUTP(top_clrtoeol);
+ putchar('\n');
+}
+
+
+/*
+ * Calculates the number of tasks in each state (running, sleeping, etc.).
+ * Calculates the CPU time in each state (system, user, nice, etc).
+ * Calculates percent cpu usage for each task.
+ */
+static void do_stats(proc_t** p, float elapsed_time, int pass)
+{
+ proc_t *this;
+ int arrindex, total_time, cpumap, i, n = 0;
+ int sleeping = 0, stopped = 0, zombie = 0, running = 0;
+ double system_ticks, user_ticks, nice_ticks, idle_ticks;
+ static int prev_count = 0;
+ int systime, usrtime;
+
+ /* start with one page as a reasonable allocate size */
+ static int save_history_size =
+ sizeof(long)*1024 / sizeof(struct save_hist);
+ static struct save_hist *save_history;
+ struct save_hist *New_save_hist;
+
+ if (!save_history)
+ save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+ New_save_hist = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+
+ /*
+ * Make a pass through the data to get stats.
+ */
+ arrindex = 0;
+ while (p[n]->pid != -1) {
+ this = p[n];
+ switch (this->state) {
+ case 'S':
+ case 'D':
+ sleeping++;
+ break;
+ case 'T':
+ stopped++;
+ break;
+ case 'Z':
+ zombie++;
+ break;
+ case 'R':
+ running++;
+ break;
+ default:
+ /* Don't know how to handle this one. */
+ break;
+ }
+
+ /*
+ * Calculate time in this process. Time is sum of user time
+ * (usrtime) plus system time (systime).
+ */
+ total_time = this->utime + this->stime;
+ if (arrindex >= save_history_size) {
+ save_history_size *= 2;
+ save_history = xrealloc(save_history, sizeof(struct save_hist)*save_history_size);
+ New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size);
+ }
+ New_save_hist[arrindex].ticks = total_time;
+ New_save_hist[arrindex].pid = this->pid;
+ systime = this->stime;
+ usrtime = this->utime;
+ New_save_hist[arrindex].stime = systime;
+ New_save_hist[arrindex].utime = usrtime;
+
+ /* find matching entry from previous pass */
+ for (i = 0; i < prev_count; i++) {
+ if (save_history[i].pid == this->pid) {
+ total_time -= save_history[i].ticks;
+ systime -= save_history[i].stime;
+ usrtime -= save_history[i].utime;
+
+ i = prev_count;
+ }
+ }
+
+ /*
+ * Calculate percent cpu time for this task.
+ */
+ this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time;
+ if (this->pcpu > 999)
+ this->pcpu = 999;
+
+ arrindex++;
+ n++;
+ }
+
+ /*
+ * Display stats.
+ */
+ if (pass > 0 && show_stats) {
+ printf("%d processes: %d sleeping, %d running, %d zombie, "
+ "%d stopped",
+ n, sleeping, running, zombie, stopped);
+ PUTP(top_clrtoeol);
+ putchar('\n');
+ four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks);
+ printf("CPU states:"
+ " %# 5.1f%% user, %# 5.1f%% system,"
+ " %# 5.1f%% nice, %# 5.1f%% idle",
+ user_ticks,
+ system_ticks,
+ nice_ticks,
+ idle_ticks
+ );
+ PUTP(top_clrtoeol);
+ putchar('\n');
+ }
+ /*
+ * Save this frame's information.
+ */
+ for (i = 0; i < n; i++) {
+ /* copy the relevant info for the next pass */
+ save_history[i].pid = New_save_hist[i].pid;
+ save_history[i].ticks = New_save_hist[i].ticks;
+ save_history[i].stime = New_save_hist[i].stime;
+ save_history[i].utime = New_save_hist[i].utime;
+ }
+ free(New_save_hist);
+
+ prev_count = n;
+ qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+}
+
+
+/*
+ * Process keyboard input during the main loop
+ */
+static void do_key(char c)
+{
+ int numinput, i;
+ char rcfile[MAXNAMELEN];
+ FILE *fp;
+
+ /*
+ * First the commands which don't require a terminal mode switch.
+ */
+ if (c == 'q')
+ end(0);
+ else if (c == ' ')
+ return;
+ else if (c == 12) {
+ clear_screen();
+ return;
+ } else if (c == 'I') {
+ Irixmode=(Irixmode) ? 0 : 1;
+ return;
+ }
+
+ /*
+ * Switch the terminal to normal mode. (Will the original
+ * attributes always be normal? Does it matter? I suppose the
+ * shell will be set up the way the user wants it.)
+ */
+ if (!Batch) tcsetattr(0, TCSANOW, &Savetty);
+
+ /*
+ * Handle the rest of the commands.
+ */
+ switch (c) {
+ case '?':
+ case 'h':
+ PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr);
+ printf("Proc-Top Revision 1.2");
+ PUTP(me); putchar('\n');
+ printf("Secure mode ");
+ PUTP(md);
+ fputs(Secure ? "on" : "off", stdout);
+ PUTP(me);
+ fputs("; cumulative mode ", stdout);
+ PUTP(md);
+ fputs(Cumulative ? "on" : "off", stdout);
+ PUTP(me);
+ fputs("; noidle mode ", stdout);
+ PUTP(md);
+ fputs(Noidle ? "on" : "off", stdout);
+ PUTP(me);
+ fputs("\n\n", stdout);
+ printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
+ if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+ (void) getchar();
+ break;
+ case 'i':
+ Noidle = !Noidle;
+ SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
+ break;
+ case 'u':
+ SHOWMESSAGE(("Which User (Blank for All): "));
+ strcpy(CurrUser,getstr());
+ break;
+ case 'k':
+ if (Secure)
+ SHOWMESSAGE(("\aCan't kill in secure mode"));
+ else {
+ int pid, signo;
+ PUTP(md);
+ SHOWMESSAGE(("PID to kill: "));
+ pid = getint();
+ if (pid == BAD_INPUT)
+ break;
+ PUTP(top_clrtoeol);
+ SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid));
+ PUTP(me);
+ signo = getsig();
+ /* FIXME: -1 may mean an unknown signal */
+ if (signo == -1)
+ signo = SIGTERM;
+ if (kill(pid, signo))
+ SHOWMESSAGE(("\aKill of PID %d with %d failed: %s",
+ pid, signo, strerror(errno)));
+ }
+ break;
+ case 'l':
+ SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off"));
+ if (show_loadav) {
+ show_loadav = 0;
+ header_lines--;
+ } else {
+ show_loadav = 1;
+ header_lines++;
+ }
+ Numfields = make_header();
+ break;
+ case 'm':
+ SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off"));
+ if (show_memory) {
+ show_memory = 0;
+ header_lines -= 2;
+ } else {
+ show_memory = 1;
+ header_lines += 2;
+ }
+ Numfields = make_header();
+ break;
+ case 'M':
+ SHOWMESSAGE(("Sort by memory usage"));
+ sort_type = S_MEM;
+ reset_sort_options();
+ register_sort_function(-1, (cmp_t)mem_sort);
+ break;
+ case 'n':
+ case '#':
+ printf("Processes to display (0 for unlimited): ");
+ numinput = getint();
+ if (numinput != -1) {
+ Display_procs = numinput;
+ window_size(0);
+ }
+ break;
+ case 'r':
+ if (Secure)
+ SHOWMESSAGE(("\aCan't renice in secure mode"));
+ else {
+ int pid, val;
+
+ printf("PID to renice: ");
+ pid = getint();
+ if (pid == BAD_INPUT)
+ break;
+ PUTP(tgoto(cm, 0, header_lines - 2));
+ PUTP(top_clrtoeol);
+ printf("Renice PID %d to value: ", pid);
+ val = getint();
+ if (val == BAD_INPUT)
+ val = 10;
+ if (setpriority(PRIO_PROCESS, pid, val))
+ SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s",
+ pid, val, strerror(errno)));
+ }
+ break;
+ case 'P':
+ SHOWMESSAGE(("Sort by CPU usage"));
+ sort_type = S_PCPU;
+ reset_sort_options();
+ register_sort_function(-1, (cmp_t)pcpu_sort);
+ break;
+ case 'A':
+ SHOWMESSAGE(("Sort by age"));
+ sort_type = S_AGE;
+ reset_sort_options();
+ register_sort_function(-1, (cmp_t)age_sort);
+ break;
+ case 'N':
+ SHOWMESSAGE(("Sort numerically by pid"));
+ sort_type = S_NONE;
+ reset_sort_options();
+ break;
+ case 'c':
+ show_cmd = !show_cmd;
+ SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line"));
+ break;
+ case 'S':
+ Cumulative = !Cumulative;
+ SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off"));
+ if (Cumulative)
+ headers[22][1] = 'C';
+ else
+ headers[22][1] = ' ';
+ Numfields = make_header();
+ break;
+ case 's':
+ if (Secure)
+ SHOWMESSAGE(("\aCan't change delay in secure mode"));
+ else {
+ double tmp;
+ printf("Delay between updates: ");
+ tmp = getfloat();
+ if (!(tmp < 0))
+ Sleeptime = tmp;
+ }
+ break;
+ case 't':
+ SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off"));
+ if (show_stats) {
+ show_stats = 0;
+ header_lines -= 2;
+ } else {
+ show_stats = 1;
+ header_lines += 2;
+ }
+ Numfields = make_header();
+ break;
+ case 'T':
+ SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : ""));
+ sort_type = S_TIME;
+ reset_sort_options();
+ register_sort_function( -1, (cmp_t)time_sort);
+ break;
+ case 'f':
+ case 'F':
+ change_fields();
+ break;
+ case 'o':
+ case 'O':
+ change_order();
+ break;
+ case 'W':
+ if (Secure)
+ SHOWMESSAGE(("\aCan't write configuration in secure mode"));
+ else {
+ if (getenv("HOME")) {
+ strcpy(rcfile, getenv("HOME"));
+ strcat(rcfile, "/");
+ strcat(rcfile, RCFILE);
+ fp = fopen(rcfile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%s\n", Fields);
+ i = (int) Sleeptime;
+ if (i < 2)
+ i = 2;
+ if (i > 9)
+ i = 9;
+ fprintf(fp, "%d", i);
+ if (Secure)
+ fprintf(fp, "%c", 's');
+ if (Cumulative)
+ fprintf(fp, "%c", 'S');
+ if (!show_cmd)
+ fprintf(fp, "%c", 'c');
+ if (Noidle)
+ fprintf(fp, "%c", 'i');
+ if (!show_memory)
+ fprintf(fp, "%c", 'm');
+ if (!show_loadav)
+ fprintf(fp, "%c", 'l');
+ if (!show_stats)
+ fprintf(fp, "%c", 't');
+ if (!Irixmode)
+ fprintf(fp, "%c", 'I');
+ fprintf(fp, "\n");
+ fclose(fp);
+ SHOWMESSAGE(("Wrote configuration to %s", rcfile));
+ } else {
+ SHOWMESSAGE(("Couldn't open %s", rcfile));
+ }
+ } else {
+ SHOWMESSAGE(("Couldn't get $HOME -- not saving"));
+ }
+ }
+ break;
+ default:
+ SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c));
+ }
+
+ /*
+ * Return to raw mode.
+ */
+ if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+ return;
+}
+
+
+/*#####################################################################
+ *####### A readproctable function that uses already allocated #####
+ *####### table entries. #####
+ *#####################################################################
+ */
+#define Do(x) (flags & PROC_ ## x)
+
+static proc_t** readproctab2(int flags, proc_t** tab, ...) {
+ PROCTAB* PT = NULL;
+ static proc_t *buff;
+ int n = 0;
+ static int len = 0;
+ va_list ap;
+
+ va_start(ap, tab); /* pass through args to openproc */
+ if (Do(UID)) {
+ /* temporary variables to ensure that va_arg() instances
+ * are called in the right order
+ */
+ uid_t* u;
+ int i;
+
+ u = va_arg(ap, uid_t*);
+ i = va_arg(ap, int);
+ PT = openproc(flags, u, i);
+ }
+ else if (Do(PID)) {
+ PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+ /* work around a bug in openproc() */
+ PT->procfs = NULL;
+ /* share some process time, since we skipped opendir("/proc") */
+ usleep (50*1000);
+ }
+ else if (Do(TTY) || Do(STAT))
+ PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+ else
+ PT = openproc(flags);
+ va_end(ap);
+ buff = (proc_t *) 1;
+ while (n<len && buff) { /* read table: (i) already allocated chunks */
+ if (tab[n]->cmdline) {
+ free((void*)*tab[n]->cmdline);
+ tab[n]->cmdline = NULL;
+ }
+ buff = readproc(PT, tab[n]);
+ if (buff) n++;
+ }
+ if (buff) {
+ do { /* (ii) not yet allocated chunks */
+ tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+ buff = readproc(PT, NULL); /* final null to terminate */
+ if(buff) tab[n]=buff;
+ len++;
+ n++;
+ } while (buff); /* stop when NULL reached */
+ tab[n-1] = xcalloc(NULL, sizeof (proc_t));
+ tab[n-1]->pid=-1; /* Mark end of Table */
+ } else {
+ if (n == len) {
+ tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
+ tab[n] = xcalloc(NULL, sizeof (proc_t));
+ len++;
+ }
+ tab[n]->pid=-1; /* Use this instead of NULL when not at the end of */
+ } /* the allocated space */
+ closeproc(PT);
+ return tab;
+}
+/* top.c - Source file: show Linux processes */
/*
- * top.c - show top CPU processes
- *
- * Copyright (c) 1992 Branko Lankester
- * Copyright (c) 1992 Roger Binns
- * Copyright (c) 1997 Michael K. Johnson
- *
- * Snarfed and HEAVILY modified in december 1992 for procps
- * by Michael K. Johnson, johnsonm@sunsite.unc.edu.
- *
- * Modified Michael K. Johnson's ps to make it a top program.
- * Also borrowed elements of Roger Binns kmem based top program.
- * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com)
- * 1/93
- *
- * Modified by Michael K. Johnson to be more efficient in cpu use
- * 2/21/93
- *
- * Changed top line to use uptime for the load average. Also
- * added SIGTSTP handling. J. Cowley, 19 Mar 1993.
- *
- * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu)
- * 1994/04/02. Secure mode added. "d" option added. Argument parsing
- * improved. Switched order of tick display to user, system, nice, idle,
- * because it makes more sense that way. Style regularized (to K&R,
- * more or less). Cleaned up much throughout. Added cumulative mode.
- * Help screen improved.
- *
- * Fixed kill buglet brought to my attention by Rob Hooft.
- * Problem was mixing of stdio and read()/write(). Added
- * getnum() to solve problem.
- * 12/30/93 Michael K. Johnson
- *
- * Added toggling output of idle processes via 'i' key.
- * 3/29/94 Gregory K. Nickonov
- *
- * Fixed buglet where rawmode wasn't getting restored.
- * Added defaults for signal to send and nice value to use.
- * 5/4/94 Jon Tombs.
- *
- * Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
- * Merged previous changes to 0.8 into 0.95.
- * Allowed the use of symbolic names (e.g., "HUP") for signal input.
- * Rewrote getnum() into getstr(), getint(), getsig(), etc.
- *
- * Modified 1995 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
- * added kmem top functionality (configurable fields)
- * configurable order of process display
- * Added options for dis/enabling uptime, statistics, and memory info.
- * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields)
- *
- * Modified 1996/05/18 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
- * Use of new interface and general cleanup. The code should be far more
- * readable than before.
- *
- * Modified 1996/06/25 Zygo Blaxell <zblaxell@ultratech.net>
- * Added field scaling code for programs that run more than two hours or
- * take up more than 100 megs. We have lots of both on our production line.
- *
- * Modified 1998/02/21 Kirk Bauer <kirk@kaybee.org>
- * Added the 'u' option to display only a selected user... plus it will
- * take into account that not all 20 top processes are actually shown,
- * so it can fit more onto the screen. I think this may help the
- * 'don't show idle' mode, but I'm not sure.
- *
- * Modified 1997/07/27 & 1999/01/27 Tim Janik <timj@gtk.org>
- * added `-p' option to display specific process ids.
- * process sorting is by default disabled in this case.
- * added `N' and `A' keys to sort the tasks Numerically by pid or
- * sort them by Age (newest first).
- *
- * Modified 1999/10/22 Tim Janik <timj@gtk.org>
- * miscellaneous minor fixes, including "usage: ..." output for
- * unrecognized options.
- *
- * Modified 2000/02/07 Jakub Jelinek <jakub@redhat.com>
- * Only load System.map when we are going to display WCHAN.
- * Show possible error messages from that load using SHOWMESSAGE.
- *
- * Modified 2000/07/10 Michael K. Johnson <johnsonm@redhat.com>
- * Integrated a patch to display SMP information.
+ * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
+ * All rights reserved.
+ * 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.
*/
-
+#include <asm/param.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <curses.h>
#include <errno.h>
-#include <sys/types.h>
+#include <signal.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <time.h>
-#include <sys/ioctl.h>
-#include <pwd.h>
-#include <termcap.h>
+#include <term.h>
#include <termios.h>
-#include <signal.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <ctype.h>
-#include <setjmp.h>
-#include <stdarg.h>
-#include <sys/param.h>
-#include <locale.h>
-
-#include "proc/sysinfo.h"
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+ /*
+ I am listing precisely why each header is needed because of the
+ construction of libproc -- separate header files may not always be
+ available and function names are not normalized. We have avoided
+ some library routine(s) as overkill and have subsumed some others.
+ */
+ /* need: 1 define + dev_to_tty */
+#include "proc/devname.h"
+ /* need: (ksym.c) open_psdb_message, wchan, close_psdb (redhat only) */
#include "proc/procps.h"
-#include "proc/whattime.h"
-#include "proc/sig.h"
-#include "proc/version.h"
+ /* need: 2 types + openproc, readproc, closeproc */
#include "proc/readproc.h"
+#ifdef UGH_ITS_4_RH
+ /* need: get_signal2 */
+#include "proc/signals.h"
+#else
+ /* need: signal_name_to_number */
+#include "proc/sig.h"
+#endif
+ /* need: status */
#include "proc/status.h"
-#include "proc/devname.h"
-#include "proc/compare.h"
+ /* need: meminfo stuff */
+#include "proc/sysinfo.h"
+ /* need: procps_version */
+#include "proc/version.h"
+ /* need: sprint_uptime */
+#include "proc/whattime.h"
-#define PUTP(x) (tputs(x,1,putchar))
-#define BAD_INPUT -30
+#include "top.h"
+
+
+/*###### Miscellaneous global stuff ####################################*/
+
+ /* The original and new terminal attributes */
+static struct termios Savedtty,
+ Rawtty;
+static int Ttychanged = 0;
+
+ /* Program names used in error messages and 'rc' file names */
+static char *Myname,
+ *Myrealname;
+
+ /* The Name of the config file(s), dynamically constructed */
+static char RCfile [OURPATHSZ],
+ RCfile_Sys [SMLBUFSIZ];
+
+ /* The run-time acquired page size */
+static int Page_size;
+
+ /* SMP and Irix/Solaris mode support */
+static int Cpu_tot,
+ *Cpu_map;
+
+ /* Specific process id monitoring support */
+static pid_t Monpids [MONPIDMAX] = { 0 };
+static int Monpidsidx = 0;
+
+ /* A postponed error message */
+static char Msg_delayed [SMLBUFSIZ];
+static int Msg_awaiting = 0;
+
+ /* Configurable Display support ##################################*/
+
+static unsigned PFlags [PFLAGSSIZ];
+static char CurFields [PFLAGSSIZ],
+ ColHeadings [SMLBUFSIZ],
+ ColUsername [USRNAMSIZ];
+static int NumFields;
+
+ /* Current window size.
+ note: Max_tasks is the specified number of processes to display
+ (zero for infinite), and Max_lines is the actual number
+ which, in turn, depends on screen size and summary info. */
+static int Screen_cols, Screen_rows,
+ Max_lines, Max_tasks;
+
+ /* Number of lines needed to display the summary information
+ which is never less than 2 since it also must accomodate the
+ message line + column headings -- set soley by mkheadings! */
+static int HSum_lines;
+
+ /* Maximum length to display of a process' command name/line
+ (excluding padding due to terminfo strings) */
+static int Max_cmd;
+
+ /* Controls how long we sleep between screen updates.
+ Accurate to microseconds (when not in 'Batch' mode). */
+static float Delay_time = DEF_DELAY;
+
+ /* Special flags dealing with the kernel symbol table */
+static int No_ksyms = -1, /* set to '0' if ksym avail, '1' otherwise */
+ PSDBopen = 0; /* set to '1' if psdb opened (now postponed) */
+
+ /* The Mode flags.
+ Except for Batch and Loops, all of them are preserved in the
+ rc file. Thus, once personalized it stays personalized!
+ note: the 'letter' shown is the On condition, some default to Off */
+static int 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 */
+ Irix_mode = 1, /* 'I' - Irix vs. Solaris view (SMP-only) */
+ Sort_normal = 1, /* 'R' - reversed column sort order */
+ HSum_loadav = 1, /* 'l' - display load avg and uptime summary */
+ HSum_states = 1, /* 't' - display task/cpu(s) states summary */
+ HSum_memory = 1, /* 'm' - display memory summary */
+ Show_ctimes = 1, /* 'S' - show times as cumulative */
+ Show_cmdlin = 0, /* 'c' - show cmdline vs. name */
+ Show_idleps = 1, /* 'i' - show idle processes (all processes) */
+ Show_hicols = 0, /* 'x' - show sort column highlighted */
+ Show_hirows = 1, /* 'y' - show running task(s) highlighted */
+ Show_colors = 0, /* 'z' - show in color (vs. mono) */
+ Show_hibold = 1, /* 'b' - rows and/or col bold (vs. reverse) */
+ Show_cpusum = 1; /* '1' - show combined cpu stats (vs. each) */
+
+ /* Current current sort type (this too is preserved in the rc file)
+ * and handler -- goodbye libproc mult_lvl_cmp, etc. overkill. */
+static int Sort_type = -1;
+static QSORT_t Sort_func = NULL;
+
+ /* These are our gosh darn 'Fields' !
+ They MUST be kept in sync with pflags !! */
+static FTAB_t Fieldstab[] = {
+/* head fmts width scale sort desc
+ ----------- ------- ------ ------ ------ ---------------------- */
+ { " PID ", "%5d ", -1, -1, S_PID, "Process Id" },
+ { " PPID ", "%5d ", -1, -1, -1, "Parent Process Pid" },
+ { " UID ", "%4d ", -1, -1, -1, "User Id" },
+ { "USER ", "%-8.8s ", -1, -1, S_USR, "User Name" },
+ { "GROUP ", "%-8.8s ", -1, -1, -1, "Group Name" },
+ { "TTY ", "%-8.8s ", 8, -1, S_TTY, "Controlling Tty" },
+ { " PR ", "%3ld ", -1, -1, -1, "Priority" },
+ { " NI ", "%3ld ", -1, -1, -1, "Nice value" },
+ { "#C ", "%2d ", -1, -1, -1, "Last used cpu (SMP)" },
+ { "%CPU ", "%#4.1f ", -1, -1, S_CPU, "CPU usage" },
+ { " TIME ", "%6.6s ", 6, -1, S_TME, "CPU Time" },
+ { " TIME+ ", "%9.9s ", 9, -1, S_TME, "CPU Time, hundredths" },
+ { "%MEM ", "%#4.1f ", -1, -1, S_MEM, "Memory usage (RES)" },
+ { " VIRT ", "%5.5s ", 5, SK_Kb, -1, "Virtual Image (kb)" },
+ { "SWAP ", "%4.4s ", 4, SK_Kb, -1, "Swapped size (kb)" },
+ { " RES ", "%4.4s ", 4, SK_Kb, S_MEM, "Resident size (kb)" },
+ { "CODE ", "%4.4s ", 4, SK_Kb, -1, "Code size (kb)" },
+ { "DATA ", "%4.4s ", 4, SK_Kb, -1, "Data+Stack size (kb)" },
+ { " SHR ", "%4.4s ", 4, SK_Kb, -1, "Shared Mem size (kb)" },
+ { "nFLT ", "%4.4s ", 4, SK_no, -1, "Page Fault count" },
+ { "nDRT ", "%4.4s ", 4, SK_no, -1, "Dirty Pages count" },
+ { "STA ", "%3.3s ", -1, -1, -1, "Process Status" },
+ /** next entry's special: '.head' will be formatted using table entry's own
+ '.fmts' plus runtime supplied conversion args! */
+ { "Command ", "%-*.*s ", -1, -1, S_CMD, "Command line/name" },
+ { "WCHAN ", "%-9.9s ", -1, -1, -1, "Sleeping in Function" },
+ /** next entry's special: the 0's will be replaced with '.'! */
+#ifdef UPCASE_HEXES
+ { "Flags ", "%08lX ", -1, -1, -1, "Task Flags <sched.h>" }
+#else
+ { "Flags ", "%08lx ", -1, -1, -1, "Task Flags <sched.h>" }
+#endif
+};
+
+ /* Miscellaneous Color stuff #####################################*/
+
+ /* Our three basic colors --
+ they can be manipulated by the 'tweak_colors' routine */
+static int Base_color = BASEcolor,
+ Msgs_color = MSGScolor,
+ Head_color = HEADcolor;
+
+ /* Some cap's stuff to reduce runtime calls --
+ to accomodate 'Batch' mode, they begin life as empty strings */
+static char Cap_bold [CAPBUFSIZ] = "",
+ Cap_clr_eol [CAPBUFSIZ] = "",
+ Cap_clr_eos [CAPBUFSIZ] = "",
+ Cap_clr_scr [CAPBUFSIZ] = "",
+ Cap_curs_norm [CAPBUFSIZ] = "",
+ Cap_curs_huge [CAPBUFSIZ] = "",
+ Cap_home [CAPBUFSIZ] = "",
+ Cap_norm [CAPBUFSIZ] = "",
+ Cap_reverse [CAPBUFSIZ] = "",
+ Caps_off [CAPBUFSIZ] = "";
+static char Sum_color [CLRBUFSIZ] = "",
+ Msg_color [CLRBUFSIZ] = "",
+ Pmt_color [CLRBUFSIZ] = "",
+ Hdr_color [CLRBUFSIZ] = "",
+ Row_color_norm [CLRBUFSIZ] = "",
+ Row_color_high [CLRBUFSIZ] = "";
+static int Cap_can_goto = 0,
+ Len_row_high = 0,
+ Len_row_norm = 0;
+
+ /* Development/debug stuff #######################################*/
+
+#ifdef TRAK_MAXCAPS
+static int Max_pads = 0, Min_pads = MAXINT,
+ Max_rbuf = 0, Min_rbuf = MAXINT;
+#endif
+
+#ifdef TRAK_MAXBUFS
+ BUF2INT(fmtmk, buf)
+ BUF2INT(show_special, tmp)
+ BUF2INT(ask_str, buf)
+ BUF2INT(scale_num, buf)
+ BUF2INT(scale_tics, buf)
+ BUF2INT(std_err, buf)
+ BUF2INT(frame_states, tmp)
+ BUF2INT(mkcol, tmp)
+ BUF2INT(show_a_task, cbuf)
+ BUF2INT(show_a_task, rbuf)
+ BUF2INT(rcfiles_read, fbuf)
+ BUF2INT(rcfiles_read, RCfile)
+ BUF2INT(rcfiles_read, RCfile_Sys)
+ BUF2INT(do_key, ColUsername)
+ BUF2INT(mkheadings, CurFields)
+ BUF2INT(mkheadings, ColHeadings)
+ BUF2INT(main, not_really_tmp)
+#endif
+
+\f
+/*###### Sort callbacks ################################################*/
+
+static int sort_cmd (proc_t **P, proc_t **Q)
+{
+ /* if a process doesn't have a cmdline, we'll consider it a kernel thread
+ -- since show_a_task gives such tasks special treatment, we must too */
+ if (Show_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
+ if (!(*P)->cmdline) return SORT_lt;
+ if (!(*Q)->cmdline) return SORT_gt;
+ if ( 0 > strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) )
+ return SORT_lt;
+ if ( 0 < strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) )
+ return SORT_gt;
+ } else {
+ /* this part also handles the compare if both are kernel threads */
+ if ( 0 > strcmp((*P)->cmd, (*Q)->cmd) ) return SORT_lt;
+ if ( 0 < strcmp((*P)->cmd, (*Q)->cmd) ) return SORT_gt;
+ }
+ return 0;
+}
-#include "top.h" /* new header for top specific things */
-static int *cpu_mapping;
-static int nr_cpu;
+static int sort_cpu (proc_t **P, proc_t **Q)
+{
+ if ( (*P)->pcpu < (*Q)->pcpu ) return SORT_lt;
+ if ( (*P)->pcpu > (*Q)->pcpu ) return SORT_gt;
+ return 0;
+}
-/*#######################################################################
- *#### Startup routines: parse_options, get_options, ##############
- *#### setup_terminal and main ##############
- *#######################################################################
- */
- /*
- * parse the options string as read from the config file(s).
- * if top is in secure mode, disallow changing of the delay time between
- * screen updates.
- */
-static void parse_options(char *Options, int secure)
+static int sort_mem (proc_t **P, proc_t **Q)
{
- int i;
- for (i = 0; i < strlen(Options); i++) {
- switch (Options[i]) {
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (!secure)
- Sleeptime = (float) Options[i] - '0';
- break;
- case 'S':
- Cumulative = 1;
- headers[22][1] = 'C';
- break;
- case 's':
- Secure = 1;
- break;
- case 'i':
- Noidle = 1;
- break;
- case 'm':
- show_memory = 0;
- header_lines -= 2;
- break;
- case 'M':
- sort_type = S_MEM;
- reset_sort_options();
- register_sort_function( -1, (cmp_t)mem_sort);
- break;
- case 'l':
- show_loadav = 0;
- header_lines -= 1;
- break;
- case 'P':
- sort_type = S_PCPU;
- reset_sort_options();
- register_sort_function( -1, (cmp_t)pcpu_sort);
- break;
- case 'N':
- sort_type = S_NONE;
- reset_sort_options();
- break;
- case 'A':
- sort_type = S_AGE;
- reset_sort_options();
- register_sort_function( -1, (cmp_t)age_sort);
- break;
- case 't':
- show_stats = 0;
- header_lines -= 2;
- break;
- case 'T':
- sort_type = S_TIME;
- reset_sort_options();
- register_sort_function( -1, (cmp_t)time_sort);
- break;
- case 'c':
- show_cmd = 0;
- break;
- case '\n':
- break;
- case 'I':
- Irixmode = 0;
- break;
- default:
- fprintf(stderr, "Wrong configuration option %c\n", i);
- exit(1);
- break;
- }
- }
+ if ( (*P)->resident < (*Q)->resident ) return SORT_lt;
+ if ( (*P)->resident > (*Q)->resident ) return SORT_gt;
+ /* still equal? we'll try to fix that... */
+ if ( (*P)->size < (*Q)->size ) return SORT_lt;
+ if ( (*P)->size > (*Q)->size ) return SORT_gt;
+ return 0;
}
-/*
- * Read the configuration file(s). There are two files, once SYS_TOPRC
- * which should only contain the secure switch and a sleeptime
- * value iff ordinary users are to use top in secure mode only.
- *
- * The other file is $HOME/RCFILE.
- * The configuration file should contain two lines (any of which may be
- * empty). The first line specifies the fields that are to be displayed
- * in the order you want them to. Uppercase letters specify fields
- * displayed by default, lowercase letters specify fields not shown by
- * default. The order of the letters in this line corresponds to the
- * order of the displayed fileds.
- *
- * all Options but 'q' can be read from this config file
- * The delay time option syntax differs from the commandline syntax:
- * only integer values between 2 and 9 seconds are recognized
- * (this is for standard configuration, so I think this should do).
- *
- * usually this file is not edited by hand, but written from top using
- * the 'W' command.
- */
-static void get_options(void)
+static int sort_pid (proc_t **P, proc_t **Q)
{
- FILE *fp;
- char *pt;
- char *rcfile = NULL; /* path to rc file... */
- char *home = NULL; /* path of user's home directory... */
- size_t home_length = 0; /* length of path... */
- char Options[256] = "";
- int i;
-
- nr_cpu = sysconf (_SC_NPROCESSORS_ONLN);
- if (nr_cpu < 1) nr_cpu = 1;
- cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu);
- /* read cpuname */
- for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i;
- header_lines = 6 + nr_cpu;
- fp = fopen(SYS_TOPRC, "r");
- if (fp != NULL) {
- fgets(Options, 254, fp);
- fclose(fp);
- }
- parse_options(Options, 0);
- strcpy(Options, "");
-
- if ( (home = getenv("HOME")) != NULL) {
- home_length = strlen(home);
- }
-
- if ( (rcfile = malloc(home_length + strlen(RCFILE) + 2))) {
- if (home != NULL) {
- strcpy(rcfile, home);
- strcat(rcfile, "/");
- }
- strcat(rcfile, RCFILE);
- fp = fopen(rcfile, "r");
- if (fp == NULL) {
- strcpy(Fields, DEFAULT_SHOW);
- } else {
- if (fgets(Fields, 254, fp) != NULL) {
- pt = strchr(Fields, '\n');
- if (pt) *pt = 0;
- }
- fgets(Options, 254, fp);
- fclose(fp);
- }
+ if ( (*P)->pid < (*Q)->pid ) return SORT_lt;
+ if ( (*P)->pid > (*Q)->pid ) return SORT_gt;
+ return 0;
+}
+
- free(rcfile);
- }
- parse_options(Options, getuid()? Secure : 0);
+static int sort_tme (proc_t **P, proc_t **Q)
+{
+ if (Show_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 0;
}
-/*
- * Set up the terminal attributes.
- */
-static void setup_terminal(void)
+
+static int sort_tty (proc_t **P, proc_t **Q)
{
- char *termtype;
- struct termios newtty;
- if (!Batch)
- termtype = getenv("TERM");
- else
- termtype = "dumb";
- if (!termtype) {
- /* In theory, $TERM should never not be set, but in practice,
- some gettys don't. Fortunately, vt100 is nearly always
- correct (or pretty close). */
- termtype = "VT100";
- /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */
- /* exit(1); */
- }
-
- /*
- * Get termcap entries and window size.
- */
- if(tgetent(NULL, termtype) != 1) {
- fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n",
- termtype);
- exit(1);
- }
-
- cm = tgetstr("cm", 0);
- top_clrtobot = tgetstr("cd", 0);
- cl = tgetstr("cl", 0);
- top_clrtoeol = tgetstr("ce", 0);
- ho = tgetstr("ho", 0);
- md = tgetstr("md", 0);
- mr = tgetstr("mr", 0);
- me = tgetstr("me", 0);
-
-
- if (Batch) return; /* the rest doesn't apply to batch mode */
- if (tcgetattr(0, &Savetty) == -1) {
- perror(PROGNAME ": tcgetattr() failed");
- error_end(errno);
- }
- newtty = Savetty;
- newtty.c_lflag &= ~ICANON;
- newtty.c_lflag &= ~ECHO;
- newtty.c_cc[VMIN] = 1;
- newtty.c_cc[VTIME] = 0;
- if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) {
- printf("cannot put tty into raw mode\n");
- error_end(1);
- }
- tcgetattr(0, &Rawtty);
+ if ( (*P)->tty < (*Q)->tty ) return SORT_lt;
+ if ( (*P)->tty > (*Q)->tty ) return SORT_gt;
+ /* still equal? we'll try to fix that... */
+ return sort_pid(P, Q);
}
-static int parseint(const char *src, const char *err)
+
+static int sort_usr (proc_t **P, proc_t **Q)
{
- char *endp;
- int num;
- int len;
- num = strtol(src, &endp, 0);
- if (*endp == '\0') return num;
- /* also accept prefixes of: infinite, infinity, maximum, all */
- len = strlen(src);
- if(len<1) goto fail;
- if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX;
- if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX;
- if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX;
- if(len<4 && !strncmp(src,"all" ,len)) return INT_MAX;
-fail:
- fprintf(stderr, err, src);
- exit(1);
+ if ( 0 > strcmp((*P)->euser, (*Q)->euser) ) return SORT_lt;
+ if ( 0 < strcmp((*P)->euser, (*Q)->euser) ) return SORT_gt;
+ /* still equal? we'll try to fix that... */
+ return sort_pid(P, Q);
}
-static double parseflt(const char *src, const char *err)
+\f
+/*###### Tiny useful routine(s) ########################################*/
+
+ /*
+ * This routine isolates ALL user input and ensures that we
+ * wont be mixing I/O from stdio and low-level read() requests */
+static int chin (int ech, char *buf, unsigned cnt)
{
- char *endp;
- double num;
- int len;
- num = strtod(src, &endp);
- if (*endp == '\0') return num;
- /* also accept prefixes of: infinite, infinity, maximum, all */
- len = strlen(src);
- if(len<1) goto fail;
- if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX;
- if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX;
- if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX;
- if(len<4 && !strncmp(src,"all" ,len)) return (double)INT_MAX;
-fail:
- fprintf(stderr, err, src);
- exit(1);
+ 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);
+ }
+ /* may be the beginning of a lengthy escape sequence */
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ return rc; /* note: we do NOT produce a vaid 'string' */
}
-int main(int argc, char **argv)
+
+ /*
+ * This routine simply formats whatever the caller wants and
+ * returns a pointer to the resulting 'const char' string... */
+static const char *fmtmk (const char *fmts, ...)
{
- /* For select(2). */
- struct timeval tv;
- fd_set in;
- /* For parsing arguments. */
- char *cp;
- /* The key read in. */
- char c;
-
- struct sigaction sact;
-
- setlocale(LC_ALL, "");
- get_options();
-
- /* set to PCPU sorting */
- register_sort_function( -1, (cmp_t)pcpu_sort);
-
- /*
- * Parse arguments.
- */
- (void)argc;
- argv++;
- while (*argv) {
- cp = *argv++;
- while (*cp) {
- switch (*cp) {
- case 'd':
- if (cp[1]) {
- Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n");
- goto breakargv;
- } else if (*argv) { /* last char in an argv, use next as arg */
- Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n");
- goto breakargv;
- } else {
- fprintf(stderr, "-d requires an argument\n");
- exit(1);
- }
- break;
- case 'n':
- if (cp[1]) {
- Loops = parseint(++cp, PROGNAME ": Bad value %s'\n");
- goto breakargv;
- } else if (*argv) { /* last char in an argv, use next as arg */
- Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n");
- goto breakargv;
- }
- break;
-
- case 'q':
- if (!getuid())
- /* set priority to -10 in order to stay above kswapd */
- if (setpriority(PRIO_PROCESS, getpid(), -10)) {
- /* We check this just for paranoia. It's not
- fatal, and shouldn't happen. */
- perror(PROGNAME ": setpriority() failed");
- }
- Sleeptime = 0;
- break;
- case 'p':
- if (monpids_index >= monpids_max) {
- fprintf(stderr, PROGNAME ": More than %u process ids specified\n",
- monpids_max);
- exit(1);
- }
- if (cp[1]) {
- if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 ||
- monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
- fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
- exit(1);
- }
- } else if (*argv) { /* last char in an argv, use next as arg */
- if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 ||
- monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
- fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
- exit(1);
- }
- } else {
- fprintf(stderr, "-p requires an argument\n");
- exit(1);
- }
- if (!monpids[monpids_index])
- monpids[monpids_index] = getpid();
- /* default to no sorting when monitoring process ids */
- if (!monpids_index++) {
- sort_type = S_NONE;
- reset_sort_options();
- }
- cp = "_";
- break;
- case 'b':
- Batch = 1;
- break;
- case 'c':
- show_cmd = !show_cmd;
- break;
- case 'S':
- Cumulative = 1;
- break;
- case 'i':
- Noidle = 1;
- break;
- case 's':
- Secure = 1;
- break;
- case 'C':
- CPU_states = 1;
- break;
- case '-':
- break; /* Just ignore it */
- case 'v':
- case 'V':
- fprintf(stdout, "top (%s)\n", procps_version);
- exit(0);
- case 'h':
- fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
- exit(0);
- default:
- fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp);
- fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
- exit(1);
- }
- cp++;
- }
- breakargv:
- }
-
- if (nr_cpu > 1 && CPU_states)
- header_lines++;
-
- meminfo(); /* need kb_main_total value filled in */
-
- setup_terminal();
- window_size(0);
- /*
- * Set up signal handlers.
- */
- sact.sa_handler = end;
- sact.sa_flags = 0;
- sigemptyset(&sact.sa_mask);
- sigaction(SIGHUP, &sact, NULL);
- sigaction(SIGINT, &sact, NULL);
- sigaction(SIGQUIT, &sact, NULL);
- sact.sa_handler = stop;
- sact.sa_flags = SA_RESTART;
- sigaction(SIGTSTP, &sact, NULL);
- sact.sa_handler = window_size;
- sigaction(SIGWINCH, &sact, NULL);
- sigaction(SIGCONT, &sact, NULL);
-
- /* loop, collecting process info and sleeping */
- while (1) {
- if (Loops > 0)
- Loops--;
- /* display the tasks */
- show_procs();
- /* sleep & wait for keyboard input */
- if (Loops == 0)
- end(0);
- if (!Batch)
- {
- tv.tv_sec = Sleeptime;
- tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000;
- FD_ZERO(&in);
- FD_SET(0, &in);
- if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1)
- do_key(c);
- } else {
- sleep(Sleeptime);
- }
- }
+ static char buf[BIGBUFSIZ]; /* with help stuff, our buffer */
+ va_list va; /* requirements exceed 1k */
+
+ va_start(va, fmts);
+ vsprintf(buf, fmts, va);
+ va_end(va);
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(fmtmk, buf);
+#endif
+ return (const char *)buf;
}
-/*#######################################################################
- *#### Signal handled routines: error_end, end, stop, window_size ###
- *#### Small utilities: make_header, getstr, getint, getfloat, getsig ###
- *#######################################################################
- */
+
+ /*
+ * What need be said... */
+static char *strim (int sp, char *str)
+{
+ const char *ws = "\b\f\n\r\t\v";
+ char *p;
+
+ /* this guy was originally designed just to trim the rc file lines and
+ any 'open_psdb_message' result which arrived with an inappropriate
+ newline (thanks to 'sysmap_mmap') -- but when tabs (^I) were found
+ in some proc cmdlines, a choice was offered twix space or null... */
+ if (sp)
+ while ((p = strpbrk(str, ws))) *p = ' ';
+ else
+ if ((p = strpbrk(str, ws))) *p = 0;
+ return str;
+}
- /*
- * end when exiting with an error.
- */
-static void error_end(int rno)
+ /*
+ * This guy just facilitates Batch and protects against dumb ttys. */
+static char *tg2 (int x, int y)
{
- if (!Batch)
- tcsetattr(0, TCSAFLUSH, &Savetty);
- PUTP(tgoto(cm, 0, Lines - 1));
- fputs("\r\n", stdout);
- exit(rno);
+ return Cap_can_goto ? tgoto(cursor_address, x, y) : (char *)"\n";
}
-/*
- * Normal end of execution.
- */
-static void end(int signo)
+
+\f
+/*###### Misc Color/Highlighting support ###############################*/
+
+ /*
+ * Make the appropriate caps/color strings and set some
+ * lengths which are used to distinguish twix the displayed
+ * columns and an actual printf row!
+ * note: we avoid the use of background color so as to maximize
+ * compatibility with the user's xterm settings */
+static void capsmk (void)
{
- (void)signo;
- if (!Batch)
- tcsetattr(0, TCSAFLUSH, &Savetty);
- PUTP(tgoto(cm, 0, Lines - 1));
- fputs("\r\n", stdout);
- exit(0);
+ /* macro to test if a basic (non-color) capability is valid
+ thanks: Floyd Davidson <floyd@ptialaska.net> */
+#define tIF(s) s ? s : ""
+ static int capsdone = 0;
+
+ /* just a precaution for the future, we aren't called now... */
+ if (Batch) return;
+
+ /* these are the unchangeable puppies, so we only do 'em once */
+ if (!capsdone) {
+ strcpy(Cap_bold, tIF(enter_bold_mode));
+ strcpy(Cap_clr_eol, tIF(clr_eol));
+ strcpy(Cap_clr_eos, tIF(clr_eos));
+ strcpy(Cap_clr_scr, tIF(clear_screen));
+ strcpy(Cap_curs_huge, tIF(cursor_visible));
+ strcpy(Cap_curs_norm, tIF(cursor_normal));
+ strcpy(Cap_home, tIF(cursor_home));
+ strcpy(Cap_norm, tIF(exit_attribute_mode));
+ strcpy(Cap_reverse, tIF(enter_reverse_mode));
+ sprintf(Caps_off, "%s%s", Cap_norm, tIF(orig_pair));
+ if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
+ capsdone = 1;
+ }
+
+ /* now do the changeable guys... */
+ if (Show_colors && max_colors > 0) {
+ strcpy(Sum_color, tparm(set_a_foreground, Base_color));
+ sprintf(Msg_color, "%s%s"
+ , tparm(set_a_foreground, Msgs_color), Cap_reverse);
+ sprintf(Pmt_color, "%s%s"
+ , tparm(set_a_foreground, Msgs_color), Cap_bold);
+ sprintf(Hdr_color, "%s%s"
+ , tparm(set_a_foreground, Head_color), Cap_reverse);
+ sprintf(Row_color_norm, "%s%s"
+ , Caps_off, tparm(set_a_foreground, Base_color));
+ } else {
+ Sum_color[0] = '\0';
+ strcpy(Msg_color, Cap_reverse);
+ strcpy(Pmt_color, Cap_bold);
+ strcpy(Hdr_color, Cap_reverse);
+ strcpy(Row_color_norm, Cap_norm);
+ }
+
+ sprintf(Row_color_high, "%s%s"
+ , Row_color_norm, Show_hibold ? Cap_bold : Cap_reverse);
+ Len_row_norm = strlen(Row_color_norm);
+ Len_row_high = strlen(Row_color_high);
+
+#undef tIF
}
-/*
- * SIGTSTP catcher.
- */
-static void stop(int signo)
+
+ /*
+ * Show an error, but not right now.
+ * Due to the postponed opening of ksym, using open_psdb_message,
+ * if P_WCHAN had been selected and the program is restarted, the
+ * message would otherwise be displayed prematurely */
+static void msg_save (const char *fmts, ...)
{
- (void)signo;
- /* Reset terminal. */
- if (!Batch)
- tcsetattr(0, TCSAFLUSH, &Savetty);
- PUTP(tgoto(cm, 0, Lines - 3));
- fflush(stdout);
- raise(SIGSTOP);
- /* Later... */
- if (!Batch)
- tcsetattr (0, TCSAFLUSH, &Rawtty);
+ char tmp[SMLBUFSIZ];
+ va_list va;
+
+ va_start(va, fmts);
+ vsprintf(tmp, fmts, va);
+ va_end(va);
+ /* we'll add some extra attention grabbers to whatever this is */
+ sprintf(Msg_delayed, "\a*** %s ***", strim(0, tmp));
+ Msg_awaiting = 1;
}
-/*
- * Reads the window size and clear the window. This is called on setup,
- * and also catches SIGWINCHs, and adjusts Maxlines. Basically, this is
- * the central place for window size stuff.
- */
-static void window_size(int signo)
+
+ /*
+ * Show an error message (caller may include a '\a' for sound) */
+static void show_msg (const char *str)
{
- struct winsize ws;
- (void)signo;
- if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){
- Cols = ws.ws_col;
- Lines = ws.ws_row;
- }else{
- Cols = tgetnum("co");
- Lines = tgetnum("li");
- }
- if (!Batch)
- clear_screen();
- /*
- * calculate header size, length of cmdline field ...
- */
- Numfields = make_header();
+ printf("%s%s %s %s%s"
+ , tg2(0, MSG_line)
+ , Msg_color
+ , str
+ , Caps_off
+ , Cap_clr_eol);
+ fflush(stdout);
+ sleep(MSG_SLEEP);
+ Msg_awaiting = 0;
}
-
-/*
- * this prints a possible message from open_psdb_message
- */
-static void top_message(const char *format, ...) {
- va_list arg;
- int n;
- char buffer[512];
-
- va_start (arg, format);
- n = vsnprintf (buffer, 512, format, arg);
- va_end (arg);
- if (n > -1 && n < 512)
- SHOWMESSAGE(("%s", buffer));
+
+
+ /*
+ * Show an input prompt + larger cursor */
+static void show_pmt (const char *str)
+{
+ printf("%s%s%s: %s%s"
+ , tg2(0, MSG_line)
+ , Pmt_color
+ , str
+ , Cap_curs_huge
+ , Caps_off);
+ fflush(stdout);
}
-/*
- * this adjusts the lines needed for the header to the current value
- */
-static int make_header(void)
+
+ /*
+ * 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:
+ * \01 through \07 (more properly \0001 - \0007)
+ * 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
+ * be placed at the beginning of a "short" line. */
+static void show_special (const char *glob)
{
- int i, j;
-
- j = 0;
- for (i = 0; i < strlen(Fields); i++) {
- if (Fields[i] < 'a') {
- pflags[j++] = Fields[i] - 'A';
- if (Fields[i] == 'U' && CL_wchan_nout == -1) {
- CL_wchan_nout = 0;
- /* for correct handling of WCHAN fields, we have to do distingu
- * between kernel versions */
- /* get kernel symbol table, if needed */
- if (open_psdb_message(NULL, top_message)) {
- CL_wchan_nout = 1;
- } else {
- psdbsucc = 1;
- }
- }
- }
- }
- strcpy(Header, "");
- for (i = 0; i < j; i++)
- strcat(Header, headers[pflags[i]]);
- /* readjust window size ... */
- Maxcmd = Cols - strlen(Header) + 7;
- Maxlines = Display_procs ? Display_procs : Lines - header_lines;
- if (Maxlines > Lines - header_lines)
- Maxlines = Lines - header_lines;
- return (j);
+ static 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 */
+ char tmp[BIGBUFSIZ], *cap, *lin_end, *sub_beg, *sub_end;
+ 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(tmp, glob, (unsigned)(lin_end - glob));
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(show_special, tmp);
+#endif
+ /* zero terminate this part and prepare to parse substrings */
+ tmp[lin_end - glob] = '\0';
+ room = Screen_cols;
+ sub_beg = sub_end = tmp;
+
+ while (*sub_beg) {
+ switch (*sub_end) {
+ case '\00' : /* no end delim, captab makes normal */
+ *(sub_end + 1) = '\0'; /* extend str end, then fall through */
+ case '\01' ... '\07' :
+ cap = captab[(int)*sub_end];
+ *sub_end = '\0';
+ printf("%s%.*s%s", cap, room, sub_beg, Caps_off);
+ room -= (sub_end - sub_beg);
+ sub_beg = ++sub_end;
+ break;
+ default : /* nothin' special, just text */
+ ++sub_end;
+ }
+
+ if (0 >= room) break; /* skip substrings that won't fit */
+ } /* end: while 'subtrings' */
+
+ printf("%s\n", Cap_clr_eol); /* emulate truncated newline */
+ 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'),
+ it probably means caller wants to retain cursor position on this final
+ line -- ok then, we'll just do our 'fit-to-screen' thingy... */
+ if (strlen(glob)) printf("%.*s", Screen_cols, glob);
+ fflush(stdout);
}
+ /*
+ * Change colors used in display */
+static void tweak_colors (void)
+{
+#define kbdABORT 'q'
+#define kbdAPPLY '\n'
+ int clrssav = Show_colors, boldsav = Show_hibold,
+ basesav = Base_color, msgssav = Msgs_color, headsav = Head_color;
+ int clr = Base_color, *pclr = &Base_color;
+ char ch, tgt = 'B';
+
+ if (0 >= max_colors) {
+ show_msg("\aNo colors to map!");
+ return;
+ }
+ if (!Show_colors) {
+ Show_colors = 1;
+ capsmk();
+ }
+ printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+
+ do {
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(fmtmk(COLOR_sample
+ , Cap_home, Myname, procps_version, tgt, clr));
+ chin(0, &ch, 1);
+ switch (ch) {
+ case 'B' :
+ pclr = &Base_color;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'M' :
+ pclr = &Msgs_color;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'H' :
+ pclr = &Head_color;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case '0' ... '7' :
+ clr = ch - '0';
+ break;
+ case 'b' :
+ Show_hibold = !Show_hibold;
+ break;
+ case 'z' :
+ Show_colors = !Show_colors;
+ break;
+ }
+ *pclr = clr;
+ capsmk();
+ } while (kbdAPPLY != ch && kbdABORT != ch);
+
+ if (kbdABORT == ch) {
+ Show_colors = clrssav; Show_hibold = boldsav;
+ Base_color = basesav; Msgs_color = msgssav; Head_color = headsav;
+ capsmk();
+ }
+ putp(Cap_curs_norm);
+
+#undef kbdABORT
+#undef kbdAPPLY
+}
-/*
- * Get a string from the user; the base of getint(), et al. This really
- * ought to handle long input lines and errors better. NB: The pointer
- * returned is a statically allocated buffer, so don't expect it to
- * persist between calls.
- */
-static char *getstr(void)
+\f
+/*###### Small utility routines ########################################*/
+
+ /*
+ * Get a string from the user */
+static char *ask_str (const char *prompt)
{
- static char line[BUFSIZ]; /* BUFSIZ from <stdio.h>; arbitrary */
- int i = 0;
+ static char buf[GETBUFSIZ];
+
+ show_pmt(prompt);
+ memset(buf, '\0', sizeof(buf));
+ chin(1, buf, sizeof(buf) - 1);
+ putp(Cap_curs_norm);
- /* Must make sure that buffered IO doesn't kill us. */
- fflush(stdout);
- fflush(stdin); /* Not POSIX but ok */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(ask_str, buf);
+#endif
+ return strim(0, buf);
+}
- do {
- read(STDIN_FILENO, &line[i], 1);
- } while (line[i++] != '\n' && i < sizeof(line));
- line[--i] = 0;
- return (line);
+ /*
+ * Get a float from the user */
+static float get_float (const char *prompt)
+{
+ char *line;
+ float f;
+
+ if (!(*(line = ask_str(prompt)))) return -1;
+ /* note: we're not allowing negative floats */
+ if (strcspn(line, ".1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%f", &f);
+ return f;
}
-/*
- * Get an integer from the user. Display an error message and
- * return BAD_INPUT if it's invalid; else return the number.
- */
-static int getint(void)
+ /*
+ * Get an integer from the user */
+static int get_int (const char *prompt)
{
- char *line;
- int i;
- int r;
+ char *line;
+ int n;
+
+ if (!(*(line = ask_str(prompt)))) return -1;
+ /* note: we've got to allow negative ints (renice) */
+ if (strcspn(line, "-1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%d", &n);
+ return n;
+}
- line = getstr();
- for (i = 0; line[i]; i++) {
- if (!isdigit(line[i]) && line[i] != '-') {
- SHOWMESSAGE(("That's not a number!"));
- return (BAD_INPUT);
- }
- }
+ /*
+ * Set the number of fields/columns to display.
+ * Create the field/column headings and set maximum cmdline length.
+ * Establish the heading/summary lines currently in use.
+ * Adjust the number of tasks to display. */
+static void mkheadings (void)
+{
+ const char *h;
+ int i, needpsdb = 0;
+
+ /* build our PFlags array and establish a tentative NumFields */
+ for (i = 0, NumFields = 0; i < (int)strlen(CurFields); i++) {
+ if (isupper(CurFields[i]))
+ PFlags[NumFields++] = CurFields[i] - 'A';
+ }
+
+ /* build a preliminary ColHeadings not to exceed screen width */
+ ColHeadings[0] = '\0';
+ for (i = 0; i < NumFields; i++) {
+ h = Fieldstab[PFlags[i]].head;
+ /* oops, won't fit -- we're outta here... */
+ if (Screen_cols < (int)(strlen(ColHeadings) + strlen(h))) break;
+ strcat(ColHeadings, h);
+ }
+
+ /* establish the final NumFields and prepare to grow the command
+ column heading via Max_cmd -- it may be a fib if P_CMD wasn't
+ encountered, but that's ok because it won't be displayed anyway */
+ NumFields = i;
+ Max_cmd = Screen_cols
+ - (strlen(ColHeadings) - strlen(Fieldstab[P_CMD].head)) - 1;
+
+ /* now we can build the true run-time ColHeadings and format the
+ command column heading if P_CMD is really being displayed */
+ ColHeadings[0] = '\0';
+ for (i = 0; i < NumFields; i++) {
+ /* are we gonna' need the kernel symbol table? */
+ if (P_WCHAN == PFlags[i]) needpsdb = 1;
+ h = Fieldstab[PFlags[i]].head;
+ if (P_CMD == PFlags[i])
+ strcat(ColHeadings, fmtmk(Fieldstab[P_CMD].fmts, Max_cmd, Max_cmd, h));
+ else
+ strcat(ColHeadings, h);
+ }
+
+ /* set the number of heading lines and calc display height */
+ HSum_lines = SUMMINLINS;
+ if (HSum_loadav) HSum_lines += 1;
+ if (HSum_states) {
+ if (Show_cpusum) HSum_lines += 2;
+ /* no tellin' how darn many cpus they might have -- if they exceed
+ screen height, they'll have to suffer scroll...
+ (Max_lines may go negative, which is as good as 0) */
+ else HSum_lines += Cpu_tot + 1;
+ }
+ if (HSum_memory) HSum_lines += 2;
+
+ Max_lines = Screen_rows - HSum_lines;
+ if (Max_tasks && Max_tasks < Max_lines)
+ Max_lines = Max_tasks;
+
+ /* do we (still) need the kernel symbol table? */
+ if (needpsdb) {
+ if (-1 == No_ksyms) {
+ No_ksyms = 0;
+ if (open_psdb_message(NULL, msg_save))
+ /* why so counter-intuitive, couldn't open_psdb_message
+ mirror sysmap_mmap -- that func does all the work anyway? */
+ No_ksyms = 1;
+ else
+ PSDBopen = 1;
+ }
+ }
+#ifdef UGH_ITS_4_RH
+ else if (PSDBopen) {
+ close_psdb();
+ PSDBopen = 0;
+ No_ksyms = -1;
+ }
+#endif
+
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(mkheadings, CurFields);
+ MAXBUFS(mkheadings, ColHeadings);
+#endif
+}
- /* An empty line is a legal error (hah!). */
- if (!line[0])
- return (BAD_INPUT);
- sscanf(line, "%d", &r);
- return (r);
+ /*
+ * 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 */
+static char *scale_num (unsigned num, const unsigned width, const unsigned type)
+{
+ /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
+ static double scale[] = { 1024, 1024*1024, 1024*1024*1024, 0 };
+ /* kilo, mega, giga, none */
+#ifdef UPCASE_SCALE
+ static char nextup[] = { 'K', 'M', 'G', 0 };
+#else
+ static char nextup[] = { 'k', 'm', 'g', 0 };
+#endif
+ static char buf[TNYBUFSIZ];
+ double *dp;
+ char *up;
+
+ /* try an unscaled version first... */
+ sprintf(buf, "%d", num);
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(scale_num, buf);
+#endif
+ if (strlen(buf) <= width)
+ return buf;
+
+ /* now try successively higher types until it fits */
+ for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
+ /* the most accurate version */
+ sprintf(buf, "%.1f%c", num / *dp, *up);
+ if (strlen(buf) <= width)
+ return buf;
+ /* the integer version */
+ sprintf(buf, "%d%c", (int)(num / *dp), *up);
+ if (strlen(buf) <= width)
+ return buf;
+ }
+ /* well shoot, this outta' fit... */
+ return (char *)"?";
}
-/*
- * Get a float from the user. Just like getint().
- */
-static float getfloat(void)
+ /*
+ * Do some scaling stuff.
+ * Format 'tics' to fit 'width' */
+static char *scale_tics (TICS_t tics, const unsigned width)
{
- char *line;
- int i;
- float r;
- char *savelocale;
-
- line = getstr();
-
- for (i = 0; line[i]; i++) {
- if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') {
- SHOWMESSAGE(("That's not a float!"));
- return (BAD_INPUT);
- }
- }
-
- /* An empty line is a legal error (hah!). */
- if (!line[0])
- return (BAD_INPUT);
-
- savelocale = setlocale(LC_NUMERIC, NULL);
- setlocale(LC_NUMERIC, "C");
- sscanf(line, "%f", &r);
- setlocale(LC_NUMERIC, savelocale);
- return (r);
+ static struct {
+ unsigned div;
+ const char *fmt;
+ } ttab[] = {
+ /* minutes hours days weeks */
+#ifdef UPCASE_SCALE
+ { 60, "%uM" }, { 60, "%uH" }, { 24, "%uD" }, { 7, "%uW" }
+#else
+ { 60, "%um" }, { 60, "%uh" }, { 24, "%ud" }, { 7, "%uw" }
+#endif
+ };
+ static char buf[TNYBUFSIZ];
+ unsigned i, t;
+
+ /* try successively higher units until it fits */
+ t = tics / Hertz;
+ sprintf(buf, "%d:%02d.%02d" /* minutes:seconds.tenths */
+ , t/60, t%60, (int)((tics*100)/Hertz)%100);
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(scale_tics, buf);
+#endif
+
+ if (strlen(buf) <= width)
+ return buf;
+ sprintf(buf, "%d:%02d", t/60, t%60); /* minutes:seconds */
+ if (strlen(buf) <= width)
+ return buf;
+
+ /* try successively: minutes; hours; days; weeks */
+ for (i = 0; i < MAXtbl(ttab); i++) {
+ t /= ttab[i].div;
+ sprintf(buf, ttab[i].fmt, t);
+ if (strlen(buf) <= width)
+ return buf;
+ };
+ /* well shoot, this outta' fit... */
+ return (char *)"?";
}
-/*
- * Get a signal number or name from the user. Return the number, or -1
- * on error.
- */
-static int getsig(void)
+ /*
+ * Calculate and return the elapsed time since the last update
+ * which is then used in % CPU calc's. */
+static float time_elapsed (void)
{
- char *line;
+ static struct timeval oldtimev;
+ struct timeval timev;
+ struct timezone timez;
+ 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;
+ return et;
+}
+
+\f
+/*###### Exit and Signal handled routines ##############################*/
- /* This is easy. */
- line = getstr();
- return signal_name_to_number(line);
+ /*
+ * The usual program end --
+ * called only by functions in this section. */
+static void bye_bye (int eno, const char *str)
+{
+#ifdef UGH_ITS_4_RH
+ if (PSDBopen)
+ close_psdb();
+#endif
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ printf("%s%s\n", tg2(0, Screen_rows), Cap_curs_norm);
+
+ if (str) {
+ if (eno) perror(str);
+ else fputs(str, stderr);
+ }
+#ifdef TRAK_MAXCAPS
+ fprintf(stderr,
+ "\nTRAK_MAXCAPS report:"
+ "\n\tBasic lengths:"
+ "\n\t Cap_home = %d, Cap_bold = %d, Cap_norm = %d, Cap_reverse = %d"
+ "\n\t Cap_clr_eol = %d, Cap_clr_eos = %d, Cap_clr_scr = %d"
+ "\n\t Cap_curs_norm = %d, Cap_curs_huge = %d"
+ "\n\t Caps_off = %d"
+ "\n\tColor lengths:"
+ "\n\t Sum_color = %d, Msg_color = %d, Pmt_color = %d, Hdr_color = %d"
+ "\n\t Row_color_norm = %d, Len_row_norm = '%d'"
+ "\n\t Row_color_high = %d, Len_row_high = '%d'"
+ "\n\tMax_pads = %d, Min_pads = %d"
+ "\n\tMax_rbuf = %d, Min_rbuf = %d"
+ "\n"
+ , strlen(Cap_home), strlen(Cap_bold), strlen(Cap_norm), strlen(Cap_reverse)
+ , strlen(Cap_clr_eol), strlen(Cap_clr_eos), strlen(Cap_clr_scr)
+ , strlen(Cap_curs_norm), strlen(Cap_curs_huge)
+ , strlen(Caps_off)
+ , strlen(Sum_color), strlen(Msg_color), strlen(Pmt_color), strlen(Hdr_color)
+ , strlen(Row_color_norm), Len_row_norm
+ , strlen(Row_color_high), Len_row_high
+ , Max_pads, Min_pads
+ , Max_rbuf, Min_rbuf);
+#endif
+#ifdef TRAK_MAXBUFS
+ fprintf(stderr,
+ "\nTRAK_MAXBUFS report:"
+ "\n\tused size\tfunction, buffer"
+ "\n\t---- ----\t-----------------------"
+ ARPTBUF(fmtmk, buf)
+ ARPTBUF(show_special, tmp)
+ ARPTBUF(ask_str, buf)
+ ARPTBUF(scale_num, buf)
+ ARPTBUF(scale_tics, buf)
+ ARPTBUF(std_err, buf)
+ ARPTBUF(frame_states, tmp)
+ ARPTBUF(mkcol, tmp)
+ ARPTBUF(show_a_task, cbuf)
+ ARPTBUF(show_a_task, rbuf)
+ ARPTBUF(rcfiles_read, fbuf)
+ ARPTBUF(rcfiles_read, RCfile)
+ ARPTBUF(rcfiles_read, RCfile_Sys)
+ ARPTBUF(do_key, ColUsername)
+ ARPTBUF(mkheadings, CurFields)
+ ARPTBUF(mkheadings, ColHeadings)
+ ARPTBUF(main, not_really_tmp)
+ "\n"
+ , AUSEBUF(fmtmk, buf), ASIZBUF(fmtmk, buf)
+ , AUSEBUF(show_special, tmp), ASIZBUF(show_special, tmp)
+ , AUSEBUF(ask_str, buf), ASIZBUF(ask_str, buf)
+ , AUSEBUF(scale_num, buf), ASIZBUF(scale_num, buf)
+ , AUSEBUF(scale_tics, buf), ASIZBUF(scale_tics, buf)
+ , AUSEBUF(std_err, buf), ASIZBUF(std_err, buf)
+ , AUSEBUF(frame_states, tmp), ASIZBUF(frame_states, tmp)
+ , AUSEBUF(mkcol, tmp), ASIZBUF(mkcol, tmp)
+ , AUSEBUF(show_a_task, cbuf), ASIZBUF(show_a_task, cbuf)
+ , AUSEBUF(show_a_task, rbuf), ASIZBUF(show_a_task, rbuf)
+ , AUSEBUF(rcfiles_read, fbuf), ASIZBUF(rcfiles_read, fbuf)
+ , AUSEBUF(rcfiles_read, RCfile) , ASIZBUF(rcfiles_read, RCfile)
+ , AUSEBUF(rcfiles_read, RCfile_Sys) , ASIZBUF(rcfiles_read, RCfile_Sys)
+ , AUSEBUF(do_key, ColUsername), ASIZBUF(do_key, ColUsername)
+ , AUSEBUF(mkheadings, CurFields), ASIZBUF(mkheadings, CurFields)
+ , AUSEBUF(mkheadings, ColHeadings), ASIZBUF(mkheadings, ColHeadings)
+ , AUSEBUF(main, not_really_tmp), ASIZBUF(main, not_really_tmp));
+#endif
+
+#if defined(TRAK_MAXCAPS) || defined(TRAK_MAXBUFS)
+ fprintf(stderr,
+ "\nbye_bye's Summary report:"
+ "\n\tprogram names:"
+ "\n\t Myrealname = %s, Myname = %s"
+ "\n\tterminal = '%s'"
+ "\n\t device = %s, ncurses = v%s"
+ "\n\t max_colors = %d, max_pairs = %d"
+ "\n\t Cap_can_goto = %s"
+ "\n\tScreen_cols = %d, Screen_rows = %d"
+ "\n\tNumFields = %d, HSum_lines = %d"
+ "\n\tMax_lines = %d, Max_cmd = %d, Max_tasks = %d"
+ "\n\tPage_size = %d"
+ "\n"
+ , Myrealname, Myname
+#ifdef PRETENDNOCAP
+ , "dumb"
+#else
+ , termname()
+#endif
+ , ttyname(STDOUT_FILENO), NCURSES_VERSION
+ , max_colors, max_pairs
+ , Cap_can_goto ? "yes" : "No!"
+ , Screen_cols, Screen_rows
+ , NumFields, HSum_lines
+ , Max_lines, Max_cmd, Max_tasks
+ , Page_size);
+#endif
+
+ if (str && !eno) eno = 1;
+ exit(eno);
}
-/*#######################################################################
- *#### Routine for sorting on used time, resident memory and %CPU #####
- *#### It would be easy to include full sorting capability as in #####
- *#### ps, but I think there is no real use for something that #####
- *#### complicated. Using register_sort_function or parse_sort_opt #####
- *#### you just have to do the natural thing and it will work. #####
- *#######################################################################
- */
-static int time_sort (proc_t **P, proc_t **Q)
+ /*
+ * Standard error handler to normalize the look of all err o/p */
+static void std_err (const char *str)
{
- if (Cumulative) {
- if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) <
- ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
- return -1;
- if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) >
- ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
- return 1;
- } else {
- if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
- return -1;
- if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
- return 1;
- }
- return 0;
+ static char buf[SMLBUFSIZ];
+
+ fflush(stdout);
+ /* we'll use our own buffer so callers can still use fmtmk() */
+ sprintf(buf, "\t%s: %s\n", Myname, str);
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(std_err, buf);
+#endif
+ if (!Ttychanged) {
+ fprintf(stderr, buf);
+ exit(1);
+ }
+ /* not to worry, he'll change our exit code to 1 due to 'buf' */
+ bye_bye(0, buf);
}
-static int pcpu_sort (proc_t **P, proc_t **Q)
+
+ /*
+ * Normal end of execution.
+ * catches:
+ * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
+static void stop (int dont_care_sig)
{
- if( (*P)->pcpu < (*Q)->pcpu ) return -1;
- if( (*P)->pcpu > (*Q)->pcpu ) return 1;
- return 0;
+ bye_bye(0, NULL);
}
-static int mem_sort (proc_t **P, proc_t **Q)
+
+ /*
+ * Suspend ourself.
+ * catches:
+ * SIGTSTP, SIGTTIN and SIGTTOU */
+static void suspend (int dont_care_sig)
{
- if( (*P)->vm_rss < (*Q)->vm_rss ) return -1;
- if( (*P)->vm_rss > (*Q)->vm_rss ) return 1;
- return 0;
+ /* reset terminal */
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ printf("%s%s", tg2(0, Screen_rows), Cap_curs_norm);
+ fflush(stdout);
+ raise(SIGSTOP);
+ /* later, after SIGCONT... */
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
}
-int age_sort (proc_t **P, proc_t **Q)
+
+ /*
+ * Set the screen dimensions and call the real workhorse.
+ * catches:
+ * SIGWINCH and SIGCONT */
+static void window_resize (int dont_care_sig)
{
- if( (*P)->start_time < (*Q)->start_time ) return -1;
- if( (*P)->start_time > (*Q)->start_time ) return 1;
- return 0;
+ struct winsize w;
+
+ Screen_cols = columns;
+ Screen_rows = lines;
+ if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w))) {
+ Screen_cols = w.ws_col;
+ Screen_rows = w.ws_row;
+ }
+ /* he'll calculate header size, length of command field, etc ... */
+ mkheadings();
}
-/*#######################################################################
- *#### Routines handling the field selection/ordering screens: ########
- *#### show_fields, change_order, change_fields ########
- *#######################################################################
- */
+\f
+/*###### Startup routines ##############################################*/
/*
- * Display the specification line of all fields. Upper case indicates
- * a displayed field, display order is according to the order of the
- * letters. A short description of each field is shown as well.
- * The description of a displayed field is marked by a leading
- * asterisk (*).
- */
-static void show_fields(void)
+ * Parse command line arguments
+ * (and force ol' main into a much needed diet)
+ * 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 user's wishes... */
+static void parse_argvs (char **argv)
{
- int i, row, col;
- char *p;
-
- clear_screen();
- PUTP(tgoto(cm, 3, 0));
- printf("Current Field Order: %s\n", Fields);
- for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) {
- row = i % (Lines - 3) + 3;
- col = i / (Lines - 3) * 40;
- PUTP(tgoto(cm, col, row));
- for (p = headers[i]; *p == ' '; ++p);
- printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A',
- p, headers2[i]);
- }
+ /* difference(s) from traditional top:
+ -q (zero delay time) eliminated as redundant, use -d0
+ -p (pid monitoring) allows comma delimited list
+ no deprecated/illegal use of 'goto' with 'breakargv:'
+ bunched args are actually handled properly and none are ignored
+ (we tolerate No whitespace and No switches -- maybe too tolerant) */
+ const char *usage = " -hv | -bcisS -d delay -n iterations -p pid [,pid ...]\n";
+ float tmp_delay = MAXFLOAT;
+ char *p;
+
+ ++argv;
+ while (*argv) {
+ char *cp = *(argv++);
+
+ while (*cp) {
+ switch (*cp) {
+ case '\0':
+ case '-':
+ break;
+ case 'b':
+ Batch = 1;
+ break;
+ case 'c':
+ Show_cmdlin = !Show_cmdlin;
+ break;
+ case 'd':
+ if (cp[1]) ++cp;
+ else if (*argv) cp = *argv++;
+ else std_err("-d requires argument");
+ /* a negative delay will be dealt with shortly... */
+ if (1 != sscanf(cp, "%f", &tmp_delay))
+ std_err(fmtmk("bad delay '%s'", cp));
+ break;
+ case 'h': case 'H':
+ case 'v': case 'V':
+ std_err(fmtmk("\t%s\nusage:\t%s%s"
+ , procps_version, Myname, usage));
+ case 'i':
+ Show_idleps = !Show_idleps;
+ break;
+ case 'n':
+ if (cp[1]) cp++;
+ else if (*argv) cp = *argv++;
+ else std_err("-n requires argument");
+ if (1 != sscanf(cp, "%d", &Loops) || 0 > Loops)
+ std_err(fmtmk("bad iterations arg '%s'", cp));
+ break;
+ case 'p':
+ if (cp[1]) cp++;
+ else if (*argv) cp = *argv++;
+ else std_err("-p requires argument");
+ do {
+ if (Monpidsidx >= MONPIDMAX)
+ std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
+ if (1 != sscanf(cp, "%d", &Monpids[Monpidsidx])
+ || 0 > Monpids[Monpidsidx])
+ std_err(fmtmk("bad pid '%s'", cp));
+ if (!Monpids[Monpidsidx])
+ Monpids[Monpidsidx] = getpid();
+ Monpidsidx++;
+ if (!(p = strchr(cp, ',')))
+ break;
+ cp = ++p;
+ } while (*cp);
+ break;
+ case 's':
+ Secure_mode = 1;
+ break;
+ case 'S':
+ Show_ctimes = !Show_ctimes;
+ break;
+ default :
+ std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
+ , *cp, Myname, usage));
+
+ } /* end: switch */
+ /* advance cp and jump over any numerical args used above */
+ cp += strspn(++cp, "-.1234567890 ");
+
+ } /* end: while *cp */
+ } /* end: while *argv */
+
+ /* fixup delay time, maybe... */
+ if (MAXFLOAT != tmp_delay) {
+ if (Secure_mode || 0 > tmp_delay)
+ msg_save("Delay time Not changed");
+ else
+ Delay_time = tmp_delay;
+ }
+
+ /* set the default sort type, maybe... */
+ if (-1 == Sort_type || Monpidsidx) {
+ Sort_type = S_PID;
+ Sort_func = (QSORT_t)sort_pid;
+ }
}
-/*
- * change order of displayed fields
- */
-static void change_order(void)
+
+ /*
+ * Parse the options string as read from line two of the "local"
+ * configuration file. */
+static void parse_rc (char *opts)
{
- char c, ch, *p;
- int i;
-
- show_fields();
- for (;;) {
- PUTP(tgoto(cm, 0, 0));
- PUTP(top_clrtoeol);
- PUTP(tgoto(cm, 3, 0));
- PUTP(mr);
- printf("Current Field Order: %s", Fields);
- PUTP(me);
- putchar('\n');
- PUTP(tgoto(cm, 0, 1));
- printf("Upper case characters move a field to the left, lower case to the right");
- fflush(stdout);
- if (!Batch) { /* should always be true, but... */
- tcsetattr(0, TCSAFLUSH, &Rawtty);
- read(0, &c, 1);
- tcsetattr(0, TCSAFLUSH, &Savetty);
- }
- i = toupper(c) - 'A';
- if ((p = strchr(Fields, i + 'A')) != NULL) {
- if (isupper(c))
- p--;
- if ((p[1] != '\0') && (p >= Fields)) {
- ch = p[0];
- p[0] = p[1];
- p[1] = ch;
- }
- } else if ((p = strchr(Fields, i + 'a')) != NULL) {
- if (isupper(c))
- p--;
- if ((p[1] != '\0') && (p >= Fields)) {
- ch = p[0];
- p[0] = p[1];
- p[1] = ch;
- }
- } else {
- break;
- }
- }
- Numfields = make_header();
+ unsigned i;
+
+ for (i = 0; i < strlen(opts); i++) {
+ switch (opts[i]) {
+ case 'b':
+ Show_hibold = 1;
+ break;
+ case 'c':
+ Show_cmdlin = 1;
+ break;
+ case 'C':
+ Sort_type = S_CMD;
+ Sort_func = (QSORT_t)sort_cmd;
+ break;
+ case 'E':
+ Sort_type = S_USR;
+ Sort_func = (QSORT_t)sort_usr;
+ break;
+ case 'i':
+ Show_idleps = 1;
+ break;
+ case 'I':
+ Irix_mode = 1;
+ break;
+ case 'l':
+ HSum_loadav = 1;
+ break;
+ case 'm':
+ HSum_memory = 1;
+ break;
+ case 'M':
+ Sort_type = S_MEM;
+ Sort_func = (QSORT_t)sort_mem;
+ break;
+ case 'P':
+ Sort_type = S_PID;
+ Sort_func = (QSORT_t)sort_pid;
+ break;
+ case 'R':
+ Sort_normal = 1;
+ break;
+ case 'S':
+ Show_ctimes = 1;
+ break;
+ case 't':
+ HSum_states = 1;
+ break;
+ case 'T':
+ Sort_type = S_TME;
+ Sort_func = (QSORT_t)sort_tme;
+ break;
+ case 'U':
+ Sort_type = S_CPU;
+ Sort_func = (QSORT_t)sort_cpu;
+ break;
+ case 'x':
+ Show_hicols = 1;
+ break;
+ case 'y':
+ Show_hirows = 1;
+ break;
+ case 'Y':
+ Sort_type = S_TTY;
+ Sort_func = (QSORT_t)sort_tty;
+ break;
+ case 'z':
+ Show_colors = 1;
+ break;
+ case '1':
+ Show_cpusum = 1;
+ break;
+ case ' ' : /* these serve as rc file comments */
+ case '\t': /* and will be treated as 'eol' */
+ case '\n':
+ return;
+ default :
+ std_err(fmtmk("bad config option - '%c'", opts[i]));
+ }
+ }
}
-/*
- * toggle displayed fields
- */
-static void change_fields(void)
+
+
+ /*
+ * Build the two RC file names then try to read 'em.
+ *
+ * '/etc/RCfile_Sys' contains two lines consisting of the secure mode
+ * switch and an update interval. It's presence limits what ordinary
+ * users are allowed to do.
+ *
+ * '$HOME/RCfile' contains five lines.
+ * line 1: specifies the fields that are to be displayed
+ * and their order. Uppercase letters == displayed,
+ * lowercase letters == not shown.
+ * line 2: specifies miscellaneous display options and
+ * toggles along with the prefered sort order.
+ * line 3: specifies maximum processes, 0 == unlimited.
+ * line 4: specifies the update interval. If run in secure
+ * mode, this value will be ignored except for root.
+ * line 5: specifies the 3 basic color values. */
+static void rcfiles_read (void)
{
- int i, changed = 0;
- int row, col;
- char c, *p;
- char tmp[2] = " ";
-
- show_fields();
- for (;;) {
- PUTP(tgoto(cm, 0, 0));
- PUTP(top_clrtoeol);
- PUTP(tgoto(cm, 3, 0));
- PUTP(mr);
- printf("Current Field Order: %s", Fields);
- PUTP(me);
- putchar('\n');
- PUTP(tgoto(cm, 0, 1));
- if (!Batch) { /* should always be true, but... */
- printf("Toggle fields with a-x, any other key to return: ");
- fflush(stdout);
- tcsetattr(0, TCSAFLUSH, &Rawtty);
- read(0, &c, 1);
- tcsetattr(0, TCSAFLUSH, &Savetty);
- }
- i = toupper(c) - 'A';
- if (i >= 0 && i < sizeof headers / sizeof headers[0]) {
- row = i % (Lines - 3) + 3;
- col = i / (Lines - 3) * 40;
- PUTP(tgoto(cm, col, row));
- if ((p = strchr(Fields, i + 'A')) != NULL) { /* deselect Field */
- *p = i + 'a';
- putchar(' ');
- } else if ((p = strchr(Fields, i + 'a')) != NULL) { /* select previously */
- *p = i + 'A'; /* deselected field */
- putchar('*');
- } else { /* select new field */
- tmp[0] = i + 'A';
- strcat(Fields, tmp);
- putchar('*');
- }
- changed = 1;
- fflush(stdout);
- } else
- break;
- }
- if (changed)
- Numfields = make_header();
+ char fbuf[RCFBUFSIZ];
+ FILE *fp;
+
+ strcpy(RCfile_Sys, fmtmk("/etc/%src", Myrealname));
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, RCfile_Sys);
+#endif
+ if (getenv("HOME"))
+ strcpy(RCfile, fmtmk("%s%c", getenv("HOME"), '/'));
+ strcat(RCfile, fmtmk(".%src", Myname));
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, RCfile);
+#endif
+
+ fp = fopen(RCfile_Sys, "r");
+ if (fp) {
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #1 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ if (strchr(fbuf, 's')) Secure_mode = 1;
+
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #2 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ fclose(fp);
+ sscanf(fbuf, "%f", &Delay_time);
+ }
+
+ fp = fopen(RCfile, "r");
+ if (fp) {
+ fbuf[0] = '\0';
+ if (fgets(fbuf, sizeof(fbuf), fp)) { /* rc file, line #1 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ strcpy(CurFields, strim(0, fbuf));
+ /* Now that we've found an rc file, we'll honor those
+ preferences by first turning off everything... */
+ Irix_mode = 0;
+ HSum_states = HSum_memory = HSum_loadav = Show_cpusum = 0;
+ Show_cmdlin = Show_ctimes = Show_idleps = Sort_normal = 0;
+ Show_colors = Show_hicols = Show_hirows = Show_hibold = 0;
+
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #2 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ /* we could subsume this next guy since we're the only caller
+ -- but we're both too fat already... */
+ parse_rc(strim(0, fbuf));
+
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #3 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ sscanf(fbuf, "%d", &Max_tasks);
+
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #4 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ if (!Secure_mode || !getuid())
+ sscanf(fbuf, "%f", &Delay_time);
+
+ fbuf[0] = '\0';
+ fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #5 */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(rcfiles_read, fbuf);
+#endif
+ sscanf(fbuf, "%d,%d,%d", &Base_color, &Msgs_color, &Head_color);
+ }
+ fclose(fp);
+ }
+ /* protect against meddling leading to a possible fault --
+ shorter would kinda' work, longer ain't healthy for us! */
+ if (strlen(CurFields) != strlen(DEF_FIELDS))
+ strcpy(CurFields, DEF_FIELDS);
+
+ /* lastly, establish the true runtime secure mode */
+ Secure_mode = getuid() ? Secure_mode : 0;
}
-/* Do the scaling stuff: interprets time in seconds, formats it to
- * fit width, and returns pointer to static char*.
- */
-static char *scale_time(int t,int width)
+
+ /*
+ * Set up the terminal attributes */
+static void terminal_set (void)
{
- static char buf[100];
-
- /* Try successively higher units until it fits */
-
- sprintf(buf,"%d:%02d",t/60,t%60); /* minutes:seconds */
- if (strlen(buf)<=width)
- return buf;
-
- t/=60; /* minutes */
- sprintf(buf,"%dm",t);
- if (strlen(buf)<=width)
- return buf;
-
- t/=60; /* hours */
- sprintf(buf,"%dh",t);
- if (strlen(buf)<=width)
- return buf;
-
- t/=24; /* days */
- sprintf(buf,"%dd",t);
- if (strlen(buf)<=width)
- return buf;
-
- t/=7; /* weeks */
- sprintf(buf,"%dw",t);
- return buf; /* this is our last try;
- if it still doesn't fit, too bad. */
-
- /* FIXME: if someone has a 16-way SMP running over a year... */
+ struct termios newtty;
+
+ /* first the ncurses part... */
+#ifdef PRETENDNOCAP
+ setupterm((char *)"dumb", STDOUT_FILENO, NULL);
+#else
+ setupterm(NULL, STDOUT_FILENO, NULL);
+#endif
+ /* now our part... */
+ if (!Batch) {
+ if (-1 == tcgetattr(STDIN_FILENO, &Savedtty))
+ std_err("tcgetattr() failed");
+ capsmk();
+ newtty = Savedtty;
+ newtty.c_lflag &= ~ICANON;
+ newtty.c_lflag &= ~ECHO;
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+
+ Ttychanged = 1;
+ if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty)) {
+ putp(Cap_clr_scr);
+ std_err(fmtmk("Failed tty set: %s", strerror(errno)));
+ }
+ tcgetattr(STDIN_FILENO, &Rawtty);
+ putp(Cap_clr_scr);
+ fflush(stdout);
+ }
}
-/* scale_k(k,width,unit) - interprets k as a count, formats to fit width.
- if unit is 0, k is a byte count; 1 is a kilobyte
- count; 2 for megabytes; 3 for gigabytes.
-*/
+\f
+/*###### Field Selection/Ordering routines #############################*/
-static char *scale_k(int k,int width,int unit)
+ /*
+ * Display the current fields and their order.
+ * Upper case indicates a displayed field, display order is
+ * according to the order of the letters.
+ *
+ * A short description of each field is shown as well and is
+ * marked by a leading asterisk (*) if currently displayed.
+ *
+ * After all fields have been displayed, some extra explanatory
+ * text is then output */
+static void display_fields (void)
{
- /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
- static double scale[]={1024,1024*1024,1024*1024*1024,0};
- /* kilo, mega, giga, tera */
- static char unitletters[]={'K','M','G','T',0};
- static char buf[100];
- char *up;
- double *dp;
-
- /* Try successively higher units until it fits */
-
- sprintf(buf,"%d",k);
- if (strlen(buf)<=width)
- return buf;
-
- for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) {
- sprintf(buf,"%.1f%c",k / *dp,*up);
- if (strlen(buf)<=width)
- return buf;
- sprintf(buf,"%d%c",(int)(k / *dp),*up);
- if (strlen(buf)<=width)
- return buf;
- }
-
- /* Give up; give them what we got on our shortest attempt */
- return buf;
+ const char *p, *x;
+ int i, cmax = Screen_cols / 2, rmax = Screen_rows - 3;
+
+ /* we're relying on our callers to first clear the screen --
+ thus 'fields_toggle' can avoid screen flicker since he's
+ too lazy to handle his own asterisk (*) logic */
+ putp(Cap_bold);
+ for (i = 0; i < MAXtbl(Fieldstab); ++i) {
+ /* advance past any leading spaces */
+ for (p = Fieldstab[i].head; ' ' == *p; ++p)
+ ;
+ printf("%s%c %c: %-10s = %s"
+ , tg2((i / rmax) * cmax, (i % rmax) + 3)
+ , strchr(CurFields, i + 'A') ? '*' : ' '
+ , i + 'A'
+ , p
+ , Fieldstab[i].desc);
+ }
+ putp(Row_color_norm);
+ x = FIELDS_xtra;
+ while ((p = strchr(x, '\n'))) {
+ ++i;
+ printf("%s%.*s"
+ , tg2((i / rmax) * cmax, (i % rmax) + 3)
+ , p - x, x);
+ x = ++p;
+ }
+ putp(Caps_off);
}
-/*
- *#######################################################################
- *#### Routines handling the main top screen: ########
- *#### show_task_info, show_procs, show_memory, do_stats ########
- *#######################################################################
- */
- /*
- * Displays infos for a single task
- */
-static void show_task_info(proc_t *task)
+
+ /*
+ * Change order of displayed fields. */
+static void fields_reorder (void)
{
- int i,j;
- unsigned int t;
- char *cmdptr;
- char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p;
-
- for (i = 0; i < Numfields; i++) {
- tmp[0] = 0;
- switch (pflags[i]) {
- case P_PID:
- sprintf(tmp, "%5d ", task->pid);
- break;
- case P_PPID:
- sprintf(tmp, "%5d ", task->ppid);
- break;
- case P_EUID:
- sprintf(tmp, "%4d ", task->euid);
- break;
- case P_EUSER:
- sprintf(tmp, "%-8.8s ", task->euser);
- break;
- case P_PCPU:
- sprintf(tmp, "%4.1f ", (float)task->pcpu / 10);
- break;
- case P_LCPU:
- sprintf(tmp, "%2d ", task->processor);
- break;
- case P_PMEM: {
- unsigned pmem;
- pmem = task->vm_rss * 1000ULL / kb_main_total;
- if (pmem > 999) pmem = 999;
- sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U);
- }
- break;
- case P_TTY: {
- char outbuf[9];
- dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV);
- sprintf(tmp, "%-8.8s ", outbuf);
- }
- break;
- case P_PRI:
- sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0));
- break;
- case P_NICE:
- sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0));
- break;
- case P_PAGEIN:
- sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0));
- break;
- case P_TSIZ:
- sprintf(tmp, "%5.5s ",
- scale_k(((task->end_code - task->start_code) / 1024), 5, 1));
- break;
- case P_DSIZ:
- sprintf(tmp, "%5.5s ",
- scale_k(((task->vsize - task->end_code) / 1024), 5, 1));
- break;
- case P_SIZE:
- sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1));
- break;
- case P_TRS:
- sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1));
- break;
- case P_SWAP:
- sprintf(tmp, "%4.4s ",
- scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1));
- break;
- case P_SHARE:
- sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1));
- break;
- case P_A:
- sprintf(tmp, "%3.3s ", "NYI");
- break;
- case P_WP:
- sprintf(tmp, "%3.3s ", "NYI");
- break;
- case P_DT:
- sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0));
- break;
- case P_RSS: /* rss, not resident (which includes IO memory) */
- sprintf(tmp, "%4.4s ",
- scale_k((task->rss << CL_pg_shift), 4, 1));
- break;
- case P_WCHAN:
- if (!CL_wchan_nout)
- sprintf(tmp, "%-9.9s ", wchan(task->wchan));
- else
- sprintf(tmp, "%-9lx", task->wchan);
- break;
- case P_STAT:
- sprintf(tmp, "%-4.4s ", status(task));
- break;
- case P_TIME:
- t = (task->utime + task->stime) / Hertz;
- if (Cumulative)
- t += (task->cutime + task->cstime) / Hertz;
- sprintf(tmp, "%6.6s ", scale_time(t,6));
- break;
- case P_COMMAND:
- if (!show_cmd && task->cmdline && *(task->cmdline)) {
- j=0;
- while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){
-/* #if 0 */ /* This is useless? FIXME */
- if (j > 0)
- strcat(tmp3, " ");
-/* #endif */
- strncat(tmp3, (task->cmdline)[j], 1000);
- j++;
- }
- cmdptr = tmp3;
- } else {
- cmdptr = task->cmd;
- }
- if (strlen(cmdptr) > Maxcmd)
- cmdptr[Maxcmd - 1] = 0;
- sprintf(tmp, "%s", cmdptr);
- tmp3[0]=0;
- break;
- case P_FLAGS:
- sprintf(tmp, "%8lx ", task->flags);
- break;
- }
- strcat(tmp2, tmp);
- }
- if (strlen(tmp2) > Cols - 1)
- tmp2[Cols - 1] = 0;
-
- /* take care of cases like:
- perl -e 'foo
- bar foo bar
- foo
- # end of perl script'
- */
- for (p=tmp2;*p;++p)
- if (!isgraph(*p))
- *p=' ';
-
- printf("\n%s", tmp2);
- PUTP(top_clrtoeol);
+ static char prompt[] =
+ "Upper case characters move field left, lower case right";
+ char c, *p;
+ int i;
+
+ printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+ display_fields();
+ do {
+ show_special(fmtmk(FIELDS_current
+ , Cap_home, Myname, CurFields, prompt));
+ chin(0, &c, 1);
+ i = toupper(c) - 'A';
+ if (i < 0 || i >= MAXtbl(Fieldstab))
+ break;
+ if (((p = strchr(CurFields, i + 'A')))
+ || ((p = strchr(CurFields, i + 'a')))) {
+ if (isupper(c)) p--;
+ if (('\0' != p[1]) && (p >= CurFields)) {
+ c = p[0];
+ p[0] = p[1];
+ p[1] = c;
+ }
+ }
+ } while (1);
+ putp(Cap_curs_norm);
+ mkheadings();
}
-/*
- * This is the real program! Read process info and display it.
- * One could differentiate options of readproctable2, perhaps it
- * would be useful to support the PROC_UID and PROC_TTY
- * as command line options.
- */
-static void show_procs(void)
+
+ /*
+ * Toggle displayed fields. */
+static void fields_toggle (void)
{
- static proc_t **p_table=NULL;
- static int proc_flags;
- int count;
- int ActualLines;
- float elapsed_time;
- static int first=0;
-
- if (first==0) {
- proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT;
- if (monpids_index)
- proc_flags |= PROC_PID;
- p_table=readproctab2(proc_flags, p_table, monpids);
- elapsed_time = get_elapsed_time();
- do_stats(p_table, elapsed_time, 0);
- sleep(1);
- first=1;
- }
- if (first && Batch)
- fputs("\n\n",stdout);
- /* Display the load averages. */
- PUTP(ho);
- PUTP(md);
- if (show_loadav) {
- printf("%s", sprint_uptime());
- PUTP(top_clrtoeol);
- putchar('\n');
- }
- p_table=readproctab2(proc_flags, p_table, monpids);
- /* Immediately find out the elapsed time for the frame. */
- elapsed_time = get_elapsed_time();
- /* Display the system stats, calculate percent CPU time
- * and sort the list. */
- do_stats(p_table, elapsed_time,1);
- /* Display the memory and swap space usage. */
- show_meminfo();
- if (strlen(Header) + 2 > Cols)
- Header[Cols - 2] = 0;
- PUTP(mr);
- fputs(Header, stdout);
- PUTP(top_clrtoeol);
- PUTP(me);
-
- /*
- * Finally! Loop through to find the top task, and display it.
- * Lather, rinse, repeat.
- */
- count = 0;
- ActualLines = 0;
- while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) {
- char Stat;
-
- Stat = p_table[count]->state;
-
- if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) &&
- ( (CurrUser[0] == '\0') ||
- (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
-
- /*
- * Show task info.
- */
- show_task_info(p_table[count]);
- if (!Batch)
- ActualLines++;
- }
- count++;
- }
- PUTP(top_clrtobot);
- PUTP(tgoto(cm, 0, header_lines - 2));
- fflush(stdout);
+ static char prompt[] =
+ "Toggle fields with a-x, type any other key to return";
+ char c, *p;
+ int i;
+
+ printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+ do {
+ display_fields();
+ show_special(fmtmk(FIELDS_current
+ , Cap_home, Myname, CurFields, prompt));
+ chin(0, &c, 1);
+ i = toupper(c) - 'A';
+ if (i < 0 || i >= MAXtbl(Fieldstab))
+ break;
+ if ((p = strchr(CurFields, i + 'A')))
+ *p = i + 'a';
+ else if ((p = strchr(CurFields, i + 'a')))
+ *p = i + 'A';
+ } while (1);
+ putp(Cap_curs_norm);
+ mkheadings();
}
+\f
+/*###### Library Alternatives ##########################################*/
-/*
- * Finds the current time (in microseconds) and calculates the time
- * elapsed since the last update. This is essential for computing
- * percent CPU usage.
- */
-static float get_elapsed_time(void)
+ /*
+ * Handle our own memory stuff without the risk of leaving the
+ * user's terminal in an ugly state should things go sour. */
+static const char *alloc_msg = "Failed memory allocate (%d bytes)";
+
+static void *alloc_c (unsigned numb)
{
- struct timeval t;
- static struct timeval oldtime;
- struct timezone timez;
- float elapsed_time;
-
- gettimeofday(&t, &timez);
- elapsed_time = (t.tv_sec - oldtime.tv_sec)
- + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
- oldtime.tv_sec = t.tv_sec;
- oldtime.tv_usec = t.tv_usec;
- return (elapsed_time);
+ void * p;
+
+ if (!numb) ++numb;
+ if (!(p = calloc(1, numb)))
+ std_err(fmtmk(alloc_msg, numb));
+ return p;
}
-/*
- * Reads the memory info and displays it. Returns the total memory
- * available, for use in percent memory usage calculations.
- */
-static void show_meminfo(void)
+
+static void *alloc_r (void *q, unsigned numb)
{
- meminfo(); /* read+parse /proc/meminfo */
- if (show_memory) {
- printf(
- "Mem: %8dK total, %8dK used, %8dK free, %8dK buffers",
- kb_main_total,
- kb_main_used,
- kb_main_free,
- kb_main_buffers
- );
- PUTP(top_clrtoeol);
- putchar('\n');
- printf(
- "Swap: %8dK total, %8dK used, %8dK free, %8dK cached",
- kb_swap_total,
- kb_swap_used,
- kb_swap_free,
- kb_main_cached
- );
- PUTP(top_clrtoeol);
- putchar('\n');
- }
- PUTP(me);
- PUTP(top_clrtoeol);
- putchar('\n');
+ void *p;
+
+ if (!numb) ++numb;
+ if (!(p = realloc(q, numb)))
+ std_err(fmtmk(alloc_msg, numb));
+ return p;
}
-/*
- * Calculates the number of tasks in each state (running, sleeping, etc.).
- * Calculates the CPU time in each state (system, user, nice, etc).
- * Calculates percent cpu usage for each task.
- */
-static void do_stats(proc_t** p, float elapsed_time, int pass)
+ /*
+ * This guy is modeled on libproc's readproctab function except
+ * 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> */
+static proc_t **readprocs (proc_t **tbl)
{
- proc_t *this;
- int arrindex, total_time, cpumap, i, n = 0;
- int sleeping = 0, stopped = 0, zombie = 0, running = 0;
- double system_ticks, user_ticks, nice_ticks, idle_ticks;
- static int prev_count = 0;
- int systime, usrtime;
-
- /* start with one page as a reasonable allocate size */
- static int save_history_size =
- sizeof(long)*1024 / sizeof(struct save_hist);
- static struct save_hist *save_history;
- struct save_hist *New_save_hist;
-
- if (!save_history)
- save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
- New_save_hist = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
-
- /*
- * Make a pass through the data to get stats.
- */
- arrindex = 0;
- while (p[n]->pid != -1) {
- this = p[n];
- switch (this->state) {
- case 'S':
- case 'D':
- sleeping++;
- break;
- case 'T':
- stopped++;
- break;
- case 'Z':
- zombie++;
- break;
- case 'R':
- running++;
- break;
- default:
- /* Don't know how to handle this one. */
- break;
- }
-
- /*
- * Calculate time in this process. Time is sum of user time
- * (usrtime) plus system time (systime).
- */
- total_time = this->utime + this->stime;
- if (arrindex >= save_history_size) {
- save_history_size *= 2;
- save_history = xrealloc(save_history, sizeof(struct save_hist)*save_history_size);
- New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size);
- }
- New_save_hist[arrindex].ticks = total_time;
- New_save_hist[arrindex].pid = this->pid;
- systime = this->stime;
- usrtime = this->utime;
- New_save_hist[arrindex].stime = systime;
- New_save_hist[arrindex].utime = usrtime;
-
- /* find matching entry from previous pass */
- for (i = 0; i < prev_count; i++) {
- if (save_history[i].pid == this->pid) {
- total_time -= save_history[i].ticks;
- systime -= save_history[i].stime;
- usrtime -= save_history[i].utime;
-
- i = prev_count;
- }
- }
-
- /*
- * Calculate percent cpu time for this task.
- */
- this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time;
- if (this->pcpu > 999)
- this->pcpu = 999;
-
- arrindex++;
- n++;
- }
-
- /*
- * Display stats.
- */
- if (pass > 0 && show_stats) {
- printf("%d processes: %d sleeping, %d running, %d zombie, "
- "%d stopped",
- n, sleeping, running, zombie, stopped);
- PUTP(top_clrtoeol);
- putchar('\n');
- four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks);
- printf("CPU states:"
- " %# 5.1f%% user, %# 5.1f%% system,"
- " %# 5.1f%% nice, %# 5.1f%% idle",
- user_ticks,
- system_ticks,
- nice_ticks,
- idle_ticks
- );
- PUTP(top_clrtoeol);
- putchar('\n');
- }
- /*
- * Save this frame's information.
- */
- for (i = 0; i < n; i++) {
- /* copy the relevant info for the next pass */
- save_history[i].pid = New_save_hist[i].pid;
- save_history[i].ticks = New_save_hist[i].ticks;
- save_history[i].stime = New_save_hist[i].stime;
- save_history[i].utime = New_save_hist[i].utime;
- }
- free(New_save_hist);
-
- prev_count = n;
- qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+#define PTRsz sizeof(proc_t *) /* eyeball candy */
+#define ENTsz sizeof(proc_t)
+ static int flags = PROC_FILLMEM | PROC_FILLCMD | PROC_FILLUSR
+ | PROC_FILLSTATUS | PROC_FILLSTAT;
+ static unsigned savmax = 0; /* first time, bypass: (i) */
+ proc_t *ptsk = (proc_t *)-1; /* first time, force: (ii) */
+ unsigned curmax = 0; /* every time */
+ PROCTAB* PT;
+
+ if (Monpidsidx) {
+ PT = openproc(flags | PROC_PID, Monpids);
+ /* work around a previous bug in openproc (now corrected) */
+ PT->procfs = NULL;
+ } else
+ PT = openproc(flags);
+
+ /* i) Allocated Chunks: *Existing* table; refresh + reuse */
+ while (curmax < savmax) {
+ if (tbl[curmax]->cmdline) {
+ free(*tbl[curmax]->cmdline);
+ tbl[curmax]->cmdline = NULL;
+ }
+ if (!(ptsk = readproc(PT, tbl[curmax]))) break;
+ ++curmax;
+ }
+
+ /* ii) Unallocated Chunks: *New* or *Existing* table; extend + fill */
+ while (ptsk) {
+ /* realloc as we go, keeping 'tbl' ahead of 'currmax++' */
+ tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
+ /* here, readproc will allocate the underlying proc_t stg */
+ if ((ptsk = readproc(PT, NULL)))
+ tbl[curmax++] = ptsk;
+ }
+ closeproc(PT);
+
+ /* iii) Chunkless: make 'eot' entry, after possible extension */
+ if (curmax >= savmax) {
+ tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
+ /* here, we must allocate the underlying proc_t stg ourselves */
+ tbl[curmax] = alloc_c(ENTsz);
+ savmax = curmax + 1;
+ }
+ /* this frame's end, but not necessarily end of allocated space */
+ tbl[curmax]->pid = -1;
+ return tbl;
+
+#undef PTRsz
+#undef ENTsz
}
+\f
+/*###### Main screen routines ##########################################*/
-/*
- * Process keyboard input during the main loop
- */
-static void do_key(char c)
+ /*
+ * Process keyboard input during the main loop plus the three
+ * special immediate keys used with help processing.
+ * (thus making us only slightly recursive) */
+static void do_key (unsigned c)
{
- int numinput, i;
- char rcfile[MAXNAMELEN];
- FILE *fp;
-
- /*
- * First the commands which don't require a terminal mode switch.
- */
- if (c == 'q')
- end(0);
- else if (c == ' ')
- return;
- else if (c == 12) {
- clear_screen();
- return;
- } else if (c == 'I') {
- Irixmode=(Irixmode) ? 0 : 1;
- return;
- }
-
- /*
- * Switch the terminal to normal mode. (Will the original
- * attributes always be normal? Does it matter? I suppose the
- * shell will be set up the way the user wants it.)
- */
- if (!Batch) tcsetattr(0, TCSANOW, &Savetty);
-
- /*
- * Handle the rest of the commands.
- */
- switch (c) {
- case '?':
- case 'h':
- PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr);
- printf("Proc-Top Revision 1.2");
- PUTP(me); putchar('\n');
- printf("Secure mode ");
- PUTP(md);
- fputs(Secure ? "on" : "off", stdout);
- PUTP(me);
- fputs("; cumulative mode ", stdout);
- PUTP(md);
- fputs(Cumulative ? "on" : "off", stdout);
- PUTP(me);
- fputs("; noidle mode ", stdout);
- PUTP(md);
- fputs(Noidle ? "on" : "off", stdout);
- PUTP(me);
- fputs("\n\n", stdout);
- printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
- if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
- (void) getchar();
- break;
+#define kbdCTRL_L 12
+ /* standardized 'secure mode' errors */
+ const char *smerror = "\aCan't %s in secure mode";
+
+ switch (c) {
+ case 'b':
+ if (!Show_hicols && !Show_hirows)
+ show_msg("\aNothing to highlight!");
+ else {
+ Show_hibold = !Show_hibold;
+ }
+ capsmk();
+ break;
+
+ case 'c':
+ Show_cmdlin = !Show_cmdlin;
+ break;
+
+ case 'C':
+ Sort_type = S_CMD;
+ Sort_func = (QSORT_t)sort_cmd;
+ break;
+
+ case 'E':
+ Sort_type = S_USR;
+ Sort_func = (QSORT_t)sort_usr;
+ break;
+
+ case 'f':
+ case 'F':
+ fields_toggle();
+ break;
+
case 'i':
- Noidle = !Noidle;
- SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
- break;
- case 'u':
- SHOWMESSAGE(("Which User (Blank for All): "));
- strcpy(CurrUser,getstr());
- break;
+ Show_idleps = !Show_idleps;
+ break;
+
+ case 'I':
+ if (Cpu_tot > 1) {
+ Irix_mode = !Irix_mode;
+ show_msg(fmtmk("Irix mode %s", Irix_mode ? "On" : "Off"));
+ } else
+ show_msg("\aIrix mode requires SMP!");
+ break;
+
case 'k':
- if (Secure)
- SHOWMESSAGE(("\aCan't kill in secure mode"));
- else {
- int pid, signo;
- PUTP(md);
- SHOWMESSAGE(("PID to kill: "));
- pid = getint();
- if (pid == BAD_INPUT)
- break;
- PUTP(top_clrtoeol);
- SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid));
- PUTP(me);
- signo = getsig();
- /* FIXME: -1 may mean an unknown signal */
- if (signo == -1)
- signo = SIGTERM;
- if (kill(pid, signo))
- SHOWMESSAGE(("\aKill of PID %d with %d failed: %s",
- pid, signo, strerror(errno)));
- }
- break;
+ if (Secure_mode) {
+ show_msg(fmtmk(smerror, "kill"));
+ } else {
+ int sig, pid = get_int("PID to kill");
+
+ if (-1 != pid) {
+#ifdef UGH_ITS_4_RH
+ sig = get_signal2(
+#else
+ sig = signal_name_to_number(
+#endif
+ ask_str(fmtmk("Kill PID %d with signal [%i]"
+ , pid, DEF_SIGNAL)));
+ if (-1 == sig) sig = DEF_SIGNAL;
+ if (sig && kill(pid, sig))
+ show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s"
+ , pid, sig, strerror(errno)));
+ }
+ }
+ break;
+
case 'l':
- SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off"));
- if (show_loadav) {
- show_loadav = 0;
- header_lines--;
- } else {
- show_loadav = 1;
- header_lines++;
- }
- Numfields = make_header();
- break;
+ HSum_loadav = !HSum_loadav;
+ mkheadings();
+ break;
+
case 'm':
- SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off"));
- if (show_memory) {
- show_memory = 0;
- header_lines -= 2;
- } else {
- show_memory = 1;
- header_lines += 2;
- }
- Numfields = make_header();
- break;
+ HSum_memory = !HSum_memory;
+ mkheadings();
+ break;
+
case 'M':
- SHOWMESSAGE(("Sort by memory usage"));
- sort_type = S_MEM;
- reset_sort_options();
- register_sort_function(-1, (cmp_t)mem_sort);
- break;
+ Sort_type = S_MEM;
+ Sort_func = (QSORT_t)sort_mem;
+ break;
+
case 'n':
case '#':
- printf("Processes to display (0 for unlimited): ");
- numinput = getint();
- if (numinput != -1) {
- Display_procs = numinput;
- window_size(0);
- }
- break;
- case 'r':
- if (Secure)
- SHOWMESSAGE(("\aCan't renice in secure mode"));
- else {
- int pid, val;
-
- printf("PID to renice: ");
- pid = getint();
- if (pid == BAD_INPUT)
- break;
- PUTP(tgoto(cm, 0, header_lines - 2));
- PUTP(top_clrtoeol);
- printf("Renice PID %d to value: ", pid);
- val = getint();
- if (val == BAD_INPUT)
- val = 10;
- if (setpriority(PRIO_PROCESS, pid, val))
- SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s",
- pid, val, strerror(errno)));
- }
- break;
+ { int num;
+
+ if (-1 != (num = get_int("Processes to display (0 = unlimited)"))) {
+ Max_tasks = num;
+ window_resize(0);
+ }
+ }
+ break;
+
+ case 'o':
+ case 'O':
+ fields_reorder();
+ break;
+
case 'P':
- SHOWMESSAGE(("Sort by CPU usage"));
- sort_type = S_PCPU;
- reset_sort_options();
- register_sort_function(-1, (cmp_t)pcpu_sort);
- break;
- case 'A':
- SHOWMESSAGE(("Sort by age"));
- sort_type = S_AGE;
- reset_sort_options();
- register_sort_function(-1, (cmp_t)age_sort);
- break;
- case 'N':
- SHOWMESSAGE(("Sort numerically by pid"));
- sort_type = S_NONE;
- reset_sort_options();
- break;
- case 'c':
- show_cmd = !show_cmd;
- SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line"));
- break;
- case 'S':
- Cumulative = !Cumulative;
- SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off"));
- if (Cumulative)
- headers[22][1] = 'C';
- else
- headers[22][1] = ' ';
- Numfields = make_header();
- break;
+ Sort_type = S_PID;
+ Sort_func = (QSORT_t)sort_pid;
+ break;
+
+ case 'q':
+ stop(0);
+
+ case 'r':
+ if (Secure_mode)
+ show_msg(fmtmk(smerror, "renice"));
+ else {
+ int pid, val;
+
+ pid = get_int("PID to renice");
+ if (-1 == pid) break;
+ val = get_int(fmtmk("Renice PID %d to value", pid));
+ if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
+ show_msg(fmtmk("\aRenice of PID %d to %d failed: %s"
+ , pid, val, strerror(errno)));
+ }
+ break;
+
+ case 'R':
+ Sort_normal = !Sort_normal;
+ break;
+
case 's':
- if (Secure)
- SHOWMESSAGE(("\aCan't change delay in secure mode"));
- else {
- double tmp;
- printf("Delay between updates: ");
- tmp = getfloat();
- if (!(tmp < 0))
- Sleeptime = tmp;
- }
- break;
+ case 'd':
+ if (Secure_mode)
+ show_msg(fmtmk(smerror, "change delay"));
+ else {
+ float tmp =
+ get_float(fmtmk("Change delay from %.1f to", Delay_time));
+ if (tmp > -1) Delay_time = tmp;
+ }
+ break;
+
+ case 'S':
+ Show_ctimes = !Show_ctimes;
+ show_msg(fmtmk("Cumulative time %s", Show_ctimes ? "On" : "Off"));
+ break;
+
case 't':
- SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off"));
- if (show_stats) {
- show_stats = 0;
- header_lines -= 2;
- } else {
- show_stats = 1;
- header_lines += 2;
- }
- Numfields = make_header();
- break;
+ HSum_states = !HSum_states;
+ mkheadings();
+ break;
+
case 'T':
- SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : ""));
- sort_type = S_TIME;
- reset_sort_options();
- register_sort_function( -1, (cmp_t)time_sort);
- break;
- case 'f':
- case 'F':
- change_fields();
- break;
- case 'o':
- case 'O':
- change_order();
- break;
+ Sort_type = S_TME;
+ Sort_func = (QSORT_t)sort_tme;
+ break;
+
+ case 'u':
+ strcpy(ColUsername, ask_str("Which User (Blank for All)"));
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(do_key, ColUsername);
+#endif
+ break;
+
+ case 'U':
+ Sort_type = S_CPU;
+ Sort_func = (QSORT_t)sort_cpu;
+ break;
+
case 'W':
- if (Secure)
- SHOWMESSAGE(("\aCan't write configuration in secure mode"));
- else {
- if (getenv("HOME")) {
- strcpy(rcfile, getenv("HOME"));
- strcat(rcfile, "/");
- strcat(rcfile, RCFILE);
- fp = fopen(rcfile, "w");
- if (fp != NULL) {
- fprintf(fp, "%s\n", Fields);
- i = (int) Sleeptime;
- if (i < 2)
- i = 2;
- if (i > 9)
- i = 9;
- fprintf(fp, "%d", i);
- if (Secure)
- fprintf(fp, "%c", 's');
- if (Cumulative)
- fprintf(fp, "%c", 'S');
- if (!show_cmd)
- fprintf(fp, "%c", 'c');
- if (Noidle)
- fprintf(fp, "%c", 'i');
- if (!show_memory)
- fprintf(fp, "%c", 'm');
- if (!show_loadav)
- fprintf(fp, "%c", 'l');
- if (!show_stats)
- fprintf(fp, "%c", 't');
- if (!Irixmode)
- fprintf(fp, "%c", 'I');
- fprintf(fp, "\n");
- fclose(fp);
- SHOWMESSAGE(("Wrote configuration to %s", rcfile));
- } else {
- SHOWMESSAGE(("Couldn't open %s", rcfile));
- }
- } else {
- SHOWMESSAGE(("Couldn't get $HOME -- not saving"));
- }
- }
- break;
+ { FILE *fp = fopen(RCfile, "w");
+
+ if (fp) {
+ fprintf(fp, "%s\t\t# Fields displayed and ordering\n"
+ , CurFields);
+ fprintf(fp, "%s%s%s%s%s%s%s%s%s%s%s%s%s%c"
+ "\t\t\t\t# Misc options (spelled poorly)\n"
+ , Show_cmdlin ? "c" : "", Sort_normal ? "R" : ""
+ , HSum_loadav ? "l" : "", Show_hirows ? "y" : ""
+ , HSum_memory ? "m" : "", Show_hicols ? "x" : ""
+ , Show_idleps ? "i" : "", Show_ctimes ? "S" : ""
+ , Irix_mode ? "I" : "", HSum_states ? "t" : ""
+ , Show_hibold ? "b" : "", Show_colors ? "z" : ""
+ , Show_cpusum ? "1" : "", Sort_type);
+ fprintf(fp, "%d\t\t\t\t\t# Number of tasks shown\n"
+ , Max_tasks);
+ fprintf(fp, "%.1f\t\t\t\t\t# Delay between updates\n"
+ , Delay_time);
+ fprintf(fp, "%u,%u,%u\t\t\t\t\t# Base, Msgs & Head colors\n"
+ , Base_color, Msgs_color, Head_color);
+ fclose(fp);
+ show_msg(fmtmk("Wrote configuration to '%s'", RCfile));
+ } else
+ show_msg(fmtmk("\aFailed '%s' open: %s", RCfile, strerror(errno)));
+ }
+ break;
+
+ case 'x':
+ Show_hicols = !Show_hicols;
+ capsmk();
+ break;
+
+ case 'y':
+ Show_hirows = !Show_hirows;
+ capsmk();
+ break;
+
+ case 'Y':
+ Sort_type = S_TTY;
+ Sort_func = (QSORT_t)sort_tty;
+ break;
+
+ case 'z':
+ Show_colors = !Show_colors;
+ capsmk();
+ break;
+
+ case 'Z':
+ tweak_colors();
+ break;
+
+ case '?':
+ case 'h':
+ { char ch;
+
+ printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(fmtmk(HELP_data
+ , Myname, procps_version
+ , Secure_mode ? "On" : "Off", Show_ctimes ? "On" : "Off", Delay_time
+ , Secure_mode ? "" : HELP_unsecured));
+ chin(0, &ch, 1);
+ putp(Cap_curs_norm);
+ /* our help screen currently provides for three 'immediate' keys,
+ two of which conflict with the main process display keys */
+ switch (ch) {
+ case 'j' :
+ strcpy(CurFields, JOB_FIELDS);
+ ch = 'T'; /* force sort on time */
+ break;
+ case 'm' :
+ strcpy(CurFields, MEM_FIELDS);
+ ch = 'M'; /* force sort on %mem/res */
+ break;
+ case 'u' :
+ strcpy(CurFields, USR_FIELDS);
+ ch = 'E'; /* force sort on user */
+ break;
+ default :
+ ch = 0;
+ }
+ if (ch) {
+ /* besides resetting the sort environment with the call to us
+ below, the 'Show_' manipulations will provide our most subtle
+ hint as to what the user has just wrought */
+ Show_hibold = Show_hicols = Sort_normal = 1;
+ capsmk();
+ mkheadings();
+ do_key((unsigned)ch);
+ }
+ }
+ break;
+
+ case ' ':
+ case kbdCTRL_L:
+ putp(Cap_clr_scr);
+ break;
+
+ case '1':
+ Show_cpusum = !Show_cpusum;
+ mkheadings();
+ break;
+
+ case '\n': /* just ignore it */
+ break;
+
default:
- SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c));
- }
-
- /*
- * Return to raw mode.
- */
- if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
- return;
+ show_msg("\aUnknown command -- try 'h' for help");
+ }
+
+#undef kbdCTRL_L
+#undef smERROR
}
-/*#####################################################################
- *####### A readproctable function that uses already allocated #####
- *####### table entries. #####
- *#####################################################################
- */
-#define Do(x) (flags & PROC_ ## x)
-
-static proc_t** readproctab2(int flags, proc_t** tab, ...) {
- PROCTAB* PT = NULL;
- static proc_t *buff;
- int n = 0;
- static int len = 0;
- va_list ap;
-
- va_start(ap, tab); /* pass through args to openproc */
- if (Do(UID)) {
- /* temporary variables to ensure that va_arg() instances
- * are called in the right order
- */
- uid_t* u;
- int i;
-
- u = va_arg(ap, uid_t*);
- i = va_arg(ap, int);
- PT = openproc(flags, u, i);
- }
- else if (Do(PID)) {
- PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
- /* work around a bug in openproc() */
- PT->procfs = NULL;
- /* share some process time, since we skipped opendir("/proc") */
- usleep (50*1000);
- }
- else if (Do(TTY) || Do(STAT))
- PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
- else
- PT = openproc(flags);
- va_end(ap);
- buff = (proc_t *) 1;
- while (n<len && buff) { /* read table: (i) already allocated chunks */
- if (tab[n]->cmdline) {
- free((void*)*tab[n]->cmdline);
- tab[n]->cmdline = NULL;
- }
- buff = readproc(PT, tab[n]);
- if (buff) n++;
- }
- if (buff) {
- do { /* (ii) not yet allocated chunks */
- tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
- buff = readproc(PT, NULL); /* final null to terminate */
- if(buff) tab[n]=buff;
- len++;
- n++;
- } while (buff); /* stop when NULL reached */
- tab[n-1] = xcalloc(NULL, sizeof (proc_t));
- tab[n-1]->pid=-1; /* Mark end of Table */
- } else {
- if (n == len) {
- tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
- tab[n] = xcalloc(NULL, sizeof (proc_t));
- len++;
- }
- tab[n]->pid=-1; /* Use this instead of NULL when not at the end of */
- } /* the allocated space */
- closeproc(PT);
- return tab;
+#ifdef UGH_ITS_4_RH
+ /*
+ * Obtain memory information and display it.
+ * Return the total memory available as a page count which is
+ * then used in % memory calc's. */
+static unsigned frame_memory (void)
+{
+ /* don't be mislead by the proc/sysinfo subscripts, they're just poorly
+ chosen names for enumerations apparently designed to make source
+ lines as imbalanced and as long as possible */
+ unsigned long long **memarray;
+
+ if (!(memarray = meminfo()))
+ std_err("Failed /proc/meminfo read");
+
+ if (HSum_memory) {
+ show_special(fmtmk(MEMORY_line1
+ , BYTES_2K(memarray[meminfo_main][meminfo_total])
+ , BYTES_2K(memarray[meminfo_main][meminfo_used])
+ , BYTES_2K(memarray[meminfo_main][meminfo_free])
+ , BYTES_2K(memarray[meminfo_main][meminfo_buffers])));
+
+ show_special(fmtmk(MEMORY_line2
+ , BYTES_2K(memarray[meminfo_swap][meminfo_total])
+ , BYTES_2K(memarray[meminfo_swap][meminfo_used])
+ , BYTES_2K(memarray[meminfo_swap][meminfo_free])
+ , BYTES_2K(memarray[meminfo_total][meminfo_cached])));
+ }
+
+ return PAGE_CNT(memarray[meminfo_main][meminfo_total]);
+}
+
+#else
+
+ /*
+ * Obtain memory information and display it. */
+static void frame_memory (void)
+{
+ meminfo();
+ if (HSum_memory) {
+ show_special(fmtmk(MEMORY_line1
+ , kb_main_total
+ , kb_main_used
+ , kb_main_free
+ , kb_main_buffers));
+
+ show_special(fmtmk(MEMORY_line2
+ , kb_swap_total
+ , kb_swap_used
+ , kb_swap_free
+ , kb_main_cached));
+ }
+}
+#endif /* end: UGH_ITS_4_RH */
+
+
+ /*
+ * 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 Show_cpusum toggle */
+static void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx)
+{
+ /* we'll trim to zero if we get negative time ticks,
+ which has happened with some SMP kernels (pre-2.4?) */
+#define TRIMz(x) ((tz = (long)x) < 0 ? 0 : tz)
+ TICS_t u_tics, s_tics, n_tics, i_tics;
+ long u_frme, s_frme, n_frme, i_frme, tot_frme, tz;
+
+ if (4 != fscanf(fp, fmt, &u_tics, &n_tics, &s_tics, &i_tics))
+ std_err("Failed /proc/stat read");
+
+ u_frme = TRIMz(u_tics - cpu->u);
+ s_frme = TRIMz(s_tics - cpu->s);
+ n_frme = TRIMz(n_tics - cpu->n);
+ i_frme = TRIMz(i_tics - cpu->i);
+ tot_frme = u_frme + s_frme + n_frme + i_frme;
+ if (1 > tot_frme) tot_frme = 1;
+
+ /* display some kinda' cpu state percentages
+ (who or what is explained by the passed prefix) */
+ show_special(fmtmk(STATES_line2
+ , pfx
+ , (float)u_frme * 100 / tot_frme
+ , (float)s_frme * 100 / tot_frme
+ , (float)n_frme * 100 / tot_frme
+ , (float)i_frme * 100 / tot_frme));
+
+ /* remember for next time around */
+ cpu->u = u_tics;
+ cpu->s = s_tics;
+ cpu->n = n_tics;
+ cpu->i = i_tics;
+
+#undef TRIMz
+}
+
+
+ /*
+ * Calc the number of tasks in each state (run, sleep, etc)
+ * Calc percent cpu usage for each task (pcpu)
+ * Calc the cpu(s) percent in each state (user, system, nice, idle) */
+static void frame_states (proc_t **p, int show)
+{
+ static HIST_t *hist_sav = NULL;
+ static unsigned hist_siz;
+ static int hist_tot, showsav;
+ static CPUS_t *smpcpu;
+ HIST_t *hist_new;
+ unsigned total, running, sleeping, stopped, zombie;
+ float etime;
+ int i;
+
+ if (!hist_sav) {
+ hist_tot = 0;
+ /* room for 512 HIST_t's (if Page_size == 4k) */
+ hist_siz = (Page_size / sizeof(HIST_t));
+ hist_sav = alloc_c(hist_siz);
+ /* note: we allocate one more CPUS_t than Cpu_tot so that the last
+ slot can hold tics representing the /proc/stat cpu summary
+ (first line read) -- that slot supports Show_cpusum */
+ smpcpu = alloc_c((1 + Cpu_tot) * sizeof(CPUS_t));
+ showsav = Show_cpusum;
+ }
+
+ hist_new = alloc_c(hist_siz);
+ total = running = sleeping = stopped = zombie = 0;
+ etime = time_elapsed();
+
+ /* make a pass through the data to get stats */
+ while (-1 != p[total]->pid) {
+ TICS_t tics;
+ proc_t *this = p[total];
+
+ switch (this->state) {
+ case 'S':
+ case 'D':
+ sleeping++;
+ break;
+ case 'T':
+ stopped++;
+ break;
+ case 'Z':
+ zombie++;
+ break;
+ case 'R':
+ running++;
+ break;
+ }
+
+ if (total * sizeof(HIST_t) >= hist_siz) {
+ hist_siz += (Page_size / sizeof(HIST_t));
+ hist_sav = alloc_r(hist_sav, hist_siz);
+ hist_new = alloc_r(hist_new, hist_siz);
+ }
+
+ /* calculate time in this process; the sum of user time (utime)
+ + system time (stime) -- but PLEASE dont waste time and effort
+ calculating and saving data that goes unused, like the old top! */
+ hist_new[total].pid = this->pid;
+ hist_new[total].tics = tics = (this->utime + this->stime);
+
+ /* find matching entry from previous pass and make ticks elapsed */
+ for (i = 0; i < hist_tot; i++) {
+ if (this->pid == hist_sav[i].pid) {
+ tics -= hist_sav[i].tics;
+ break;
+ }
+ }
+
+ /* finally calculate an integer version of %cpu for this task
+ and plug it into the unfilled slot in proc_t */
+ this->pcpu = (tics * 1000 / Hertz) / etime;
+ if (this->pcpu > 999) this->pcpu = 999;
+
+ /* if in Solaris mode, adjust cpu percentage not only for the cpu
+ the process is running on, but for all cpus together */
+ if (!Irix_mode) this->pcpu /= Cpu_tot;
+
+ total++;
+ } /* end: while 'p[total]->pid' */
+
+ if (show) {
+ FILE *fp;
+
+ /* whoa, we've changed modes -- gotta' clean old histories */
+ if (Show_cpusum != showsav) {
+ /* fresh start for the last slot in the history area */
+ if (Show_cpusum) memset(&smpcpu[Cpu_tot], '\0', sizeof(CPUS_t));
+ /* fresh start for the true smpcpu history area */
+ else memset(smpcpu, '\0', Cpu_tot * sizeof(CPUS_t));
+ showsav = Show_cpusum;
+ }
+
+ /* display Task states */
+ show_special(fmtmk(STATES_line1
+ , total, running, sleeping, stopped, zombie));
+
+ /* now arrange to calculate and display states for all Cpu's */
+ if (!(fp = fopen("/proc/stat", "r")))
+ std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
+
+ if (Show_cpusum)
+ /* retrieve and display just the 1st /proc/stat line */
+ frame_smp(fp, CPU_FMTS_JUST1, &smpcpu[Cpu_tot], "Cpu(s) state:");
+ else {
+ char tmp[SMLBUFSIZ];
+
+ /* skip the 1st line, which reflects total cpu states */
+ if (!fgets(tmp, sizeof(tmp), fp))
+ std_err("Failed /proc/stat read");
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(frame_states, tmp);
+#endif
+ /* now do each cpu's states separately */
+ for (i = 0; i < Cpu_tot; i++) {
+ sprintf(tmp, "%-6scpu%-2d:" /* [ cpu states as ] */
+ , i ? " " : "State" /* 'State cpu0 : ... ' */
+ , Irix_mode ? i : Cpu_map[i]); /* ' cpu1 : ... ' */
+ frame_smp(fp, CPU_FMTS_MULTI, &smpcpu[i], tmp);
+ }
+ }
+
+ fclose(fp);
+ } /* end: if 'show' */
+
+ /* save this frame's information */
+ hist_tot = total;
+ memcpy(hist_sav, hist_new, hist_siz);
+ free(hist_new);
+ /* finally, sort the processes on whatever... */
+ qsort(p, total, sizeof(proc_t *), (QSORT_t)Sort_func);
+}
+
+
+ /*
+ * Task display *Helper* function to handle highlighted
+ * column transitions. */
+static void mkcol (unsigned idx, int sta, int *pad, char *buf, ...)
+{
+ char tmp[COLBUFSIZ];
+ va_list va;
+
+ va_start(va, buf);
+ if (!Show_hicols || Sort_type != Fieldstab[idx].sort) {
+ vsprintf(buf, Fieldstab[idx].fmts, va);
+ } else {
+ vsprintf(tmp, Fieldstab[idx].fmts, va);
+ sprintf(buf, "%s%s", Row_color_high, tmp);
+ *pad += Len_row_high;
+ if (!Show_hirows || 'R' != sta) {
+ strcat(buf, Row_color_norm);
+ *pad += Len_row_norm;
+ }
+ }
+ va_end(va);
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(mkcol, tmp);
+#endif
+}
+
+
+ /*
+ * Displays information for a single task. */
+#ifdef UGH_ITS_4_RH
+static void show_a_task (proc_t *task, unsigned mempgs)
+#else
+static void show_a_task (proc_t *task)
+#endif
+{
+ /* the following macro is used for those columns that are NOT sortable
+ so as to avoid the function call overhead since mkcol cannot be made
+ inline -- if additional sort columns are added, change the appropriate
+ switch label's usage to lower case and thus invoke the real function */
+#define MKCOL(idx,sta,pad,buf,arg) \
+ sprintf(buf, Fieldstab[idx].fmts, arg)
+ char rbuf[ROWBUFSIZ];
+ int i, x, pad;
+
+ pad = 0;
+ rbuf[0] = '\0';
+
+ for (i = 0; i < NumFields; i++) {
+ char cbuf[COLBUFSIZ];
+ unsigned f, s, w;
+
+ cbuf[0] = '\0';
+ f = PFlags[i];
+ s = Fieldstab[f].scale;
+ w = Fieldstab[f].width;
+
+ switch (f) {
+ case P_CMD:
+ { char *cmdptr, cmdnam[ROWBUFSIZ];
+
+ if (Show_cmdlin) {
+ cmdnam[0] = '\0';
+ if (task->cmdline) {
+ x = 0;
+ do {
+ /* whoa, during a kernel build, parts of the make
+ process will create cmdlines in excess of 3000 bytes
+ but *without* the typical intervening nulls */
+ strcat(cmdnam
+ , fmtmk("%.*s ", Max_cmd, task->cmdline[x++]));
+ /* whoa, gnome's xscreensaver had a ^I in his cmdline
+ creating a line wrap when the window was maximized &
+ the tab came into view -- that in turn, whacked
+ our heading lines so we'll strim those !#@*#!... */
+ strim(1, cmdnam);
+ /* enough, don't ya think? */
+ if (Max_cmd < (int)strlen(cmdnam))
+ break;
+ } while (task->cmdline[x]);
+ } else {
+ /* if this process really doesn't have a cmdline, we'll
+ consider it a kernel thread and display it uniquely
+ [ we need sort_cmd's complicity in this plot ] */
+ strcpy(cmdnam, fmtmk("( %s )", task->cmd));
+ }
+ cmdptr = cmdnam;
+ } else
+ cmdptr = task->cmd;
+ /* hurry up, before cmdptr goes out of scope... */
+ mkcol(f, task->state, &pad, cbuf, Max_cmd, Max_cmd, cmdptr);
+ }
+ break;
+ case P_CODE:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->trs), w, s));
+ break;
+ case P_CPU:
+ mkcol(f, task->state, &pad, cbuf, (float)task->pcpu / 10);
+ break;
+ case P_DATA:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->drs), w, s));
+ break;
+ case P_DIRTY:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num((unsigned)task->dt, w, s));
+ break;
+ case P_FAULT:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(task->maj_flt, w, s));
+ break;
+ case P_FLAGS:
+ MKCOL(f, task->state, &pad, cbuf, task->flags);
+ for (x = 0; x < (int)strlen(cbuf); x++)
+ if ('0' == cbuf[x]) cbuf[x] = '.';
+ break;
+ case P_GROUP:
+ MKCOL(f, task->state, &pad, cbuf, task->egroup);
+ break;
+ case P_MEM:
+ mkcol(f, task->state, &pad, cbuf
+#ifdef UGH_ITS_4_RH
+ , (float)task->resident * 100 / mempgs);
+#else
+ , (float)PAGES_2K(task->resident) * 100 / kb_main_total);
+#endif
+ break;
+ case P_NCPU:
+#ifdef UGH_ITS_4_RH
+ MKCOL(f, task->state, &pad, cbuf, task->lproc);
+#else
+ MKCOL(f, task->state, &pad, cbuf, task->processor);
+#endif
+ break;
+ case P_NI:
+ MKCOL(f, task->state, &pad, cbuf, task->nice);
+ break;
+ case P_PID:
+ mkcol(f, task->state, &pad, cbuf, task->pid);
+ break;
+ case P_PPID:
+ MKCOL(f, task->state, &pad, cbuf, task->ppid);
+ break;
+ case P_PR:
+ MKCOL(f, task->state, &pad, cbuf, task->priority);
+ break;
+ case P_RES:
+ /* 'rss' vs 'resident' (which includes IO memory) ?
+ -- we'll ensure that VIRT = SWAP + RES */
+ mkcol(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->resident), w, s));
+ break;
+ case P_SHR:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->share), w, s));
+ break;
+ case P_STA:
+ MKCOL(f, task->state, &pad, cbuf, status(task));
+ break;
+ case P_SWAP:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->size - task->resident), w, s));
+ break;
+ case P_TIME:
+ case P_TIME2:
+ { TICS_t t;
+
+ t = task->utime + task->stime;
+ if (Show_ctimes)
+ t += (task->cutime + task->cstime);
+ mkcol(f, task->state, &pad, cbuf, scale_tics(t, w));
+ }
+ break;
+ case P_TTY:
+ { char tmp[TNYBUFSIZ];
+
+ dev_to_tty(tmp, Fieldstab[f].width
+ , task->tty, task->pid, ABBREV_DEV);
+ mkcol(f, task->state, &pad, cbuf, tmp);
+ }
+ break;
+ case P_UID:
+ MKCOL(f, task->state, &pad, cbuf, task->euid);
+ break;
+ case P_USER:
+ mkcol(f, task->state, &pad, cbuf, task->euser);
+ break;
+ case P_VIRT:
+ MKCOL(f, task->state, &pad, cbuf
+ , scale_num(PAGES_2K(task->size), w, s));
+ break;
+ case P_WCHAN:
+ if (No_ksyms)
+#ifdef UPCASE_HEXES
+ MKCOL(f, task->state, &pad, cbuf
+ , fmtmk("x%08lX", (long)task->wchan));
+#else
+ MKCOL(f, task->state, &pad, cbuf
+ , fmtmk("x%08lx", (long)task->wchan));
+#endif
+ else
+ MKCOL(f, task->state, &pad, cbuf, wchan(task->wchan));
+ break;
+
+ } /* end: switch 'PFlags[i]' */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(show_a_task, cbuf);
+#endif
+ strcat(rbuf, cbuf);
+
+ } /* end: for 'NumFields' */
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(show_a_task, rbuf);
+#endif
+
+ /* This row buffer could be stuffed with parameterized strings.
+ We are thus advised to always use tputs/putp, but it works just
+ fine with good ol' printf... */
+ printf("\n%s%.*s%s%s"
+ , (Show_hirows && 'R' == task->state) ? Row_color_high : Row_color_norm
+ , Screen_cols + pad
+ , rbuf
+ , Caps_off
+ , Cap_clr_eol);
+
+#ifdef TRAK_MAXCAPS
+ if (pad > Max_pads) Max_pads = pad;
+ if (pad < Min_pads) Min_pads = pad;
+ /* now that we have TRAK_MAXBUFS, the next two duplicate some effort */
+ if ((int)strlen(rbuf) > Max_rbuf) Max_rbuf = (int)strlen(rbuf);
+ if ((int)strlen(rbuf) < Min_rbuf) Min_rbuf = (int)strlen(rbuf);
+#endif
+
+#undef MKCOL
+}
+
+
+ /*
+ * Read all process info and display it. */
+static void show_everything (void)
+{
+ static proc_t **p_table = NULL;
+ int ntask, nline;
+#ifdef UGH_ITS_4_RH
+ unsigned mempgs;
+#endif
+
+ if (!p_table) {
+ p_table = readprocs(p_table);
+ frame_states(p_table, 0);
+ sleep(1);
+ } else
+ putp(Batch ? "\n" : Cap_home);
+
+ /*
+ ** Display Load averages */
+ if (HSum_loadav)
+ show_special(fmtmk(LOADAV_line, Myname, sprint_uptime()));
+
+ /*
+ ** Display System stats (also calc 'pcpu' and sort p_table) */
+ p_table = readprocs(p_table);
+ frame_states(p_table, HSum_states);
+
+ /*
+ ** Display Memory and Swap space usage */
+#ifdef UGH_ITS_4_RH
+ mempgs = frame_memory();
+#else
+ frame_memory();
+#endif
+
+ /*
+ ** Display Headings for columns */
+ printf("%s%s%s%s%s"
+ , tg2(0, MSG_line + 1)
+ , Hdr_color
+ , ColHeadings
+ , Caps_off
+ , Cap_clr_eol);
+
+ /* Finally! Loop through to find each task, and display it ...
+ ... lather, rinse, repeat */
+ ntask = nline = 0;
+ while (-1 != p_table[ntask]->pid && nline < Max_lines) {
+ if ((Show_idleps
+ || ('S' != p_table[ntask]->state && 'Z' != p_table[ntask]->state))
+ && ((!ColUsername[0])
+ || (!strcmp(ColUsername, p_table[ntask]->euser)) ) ) {
+ /*
+ ** Display a process Row */
+#ifdef UGH_ITS_4_RH
+ show_a_task(p_table[ntask], mempgs);
+#else
+ show_a_task(p_table[ntask]);
+#endif
+ if (!Batch) ++nline;
+ }
+ ++ntask;
+ }
+
+ printf("%s%s%s", Cap_clr_eos, tg2(0, MSG_line), Cap_clr_eol);
+ fflush(stdout);
+}
+
+\f
+/*###### Entry point ###################################################*/
+
+int main (int dont_care_argc, char **argv)
+{
+ char not_really_tmp[OURPATHSZ];
+ int i;
+
+ /* setup our program name(s)... */
+ Myname = strrchr(argv[0], '/');
+ if (Myname) ++Myname; else Myname = argv[0];
+ Myrealname = Myname;
+ memset(not_really_tmp, '\0', sizeof(not_really_tmp));
+ /* proper permissions should deny symlinks to /usr/bin for ordinary
+ users, but root may have employed them -- Myrealname will be used
+ in constructing the global rc filename ... */
+ if (-1 != readlink(argv[0], not_really_tmp, sizeof(not_really_tmp) - 1)) {
+ Myrealname = strrchr(not_really_tmp, '/');
+ if (Myrealname) ++Myrealname; else Myrealname = not_really_tmp;
+#ifdef TRAK_MAXBUFS
+ MAXBUFS(main, not_really_tmp);
+#endif
+ }
+
+ /* setup some important system stuff... */
+ Page_size = getpagesize();
+ Cpu_tot = sysconf(_SC_NPROCESSORS_ONLN);
+ if (1 > Cpu_tot) Cpu_tot = 1;
+ Cpu_map = alloc_r(NULL, sizeof(int) * Cpu_tot);
+ for (i = 0; i < Cpu_tot; i++)
+ Cpu_map[i] = i;
+
+ rcfiles_read();
+ parse_argvs(argv);
+ terminal_set();
+ window_resize(0);
+
+ /* set up signal handlers */
+ signal(SIGALRM, stop);
+ signal(SIGHUP, stop);
+ signal(SIGINT, stop);
+ signal(SIGPIPE, stop);
+ signal(SIGQUIT, stop);
+ signal(SIGTERM, stop);
+ signal(SIGTSTP, suspend);
+ signal(SIGTTIN, suspend);
+ signal(SIGTTOU, suspend);
+ signal(SIGCONT, window_resize);
+ signal(SIGWINCH, window_resize);
+
+ /* loop, collecting process info and sleeping */
+ do {
+ struct timeval tv;
+ fd_set fs;
+ char c;
+
+ show_everything();
+ if (Msg_awaiting) show_msg(Msg_delayed);
+ if (0 < Loops) --Loops;
+ if (!Loops) stop(0);
+
+ if (Batch)
+ sleep((unsigned)Delay_time);
+ else {
+ /* Linux reports time not slept, so we must reinit every time */
+ tv.tv_sec = Delay_time;
+ tv.tv_usec = (Delay_time - (int)Delay_time) * 1000000;
+ FD_ZERO(&fs);
+ FD_SET(STDIN_FILENO, &fs);
+ if (0 < select(STDIN_FILENO+1, &fs, NULL, NULL, &tv)
+ && 0 < chin(0, &c, 1))
+ do_key((unsigned)c);
+ }
+ } while (1);
+
+ return 0;
}
+/* top.h - Header file: show Linux processes */
/*
- * top.h header file 1996/05/18,
- *
- * function prototypes, global data definitions and string constants.
+ * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
+ * All rights reserved.
+ * 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.
*/
+#ifndef _Itop
+#define _Itop
-static proc_t** readproctab2(int flags, proc_t** tab, ...);
-static void parse_options(char *Options, int secure);
-static void get_options(void);
-static void error_end(int rno);
-static void end(int signo);
-static void stop(int signo);
-static void window_size(int signo);
-static int make_header(void);
-static char *getstr(void);
-static int getsig(void);
-static float getfloat(void);
-static int time_sort(proc_t **P, proc_t **Q);
-static int pcpu_sort(proc_t **P, proc_t **Q);
-static int mem_sort(proc_t **P, proc_t **Q);
-static int age_sort(proc_t **P, proc_t **Q);
-static void show_fields(void);
-static void change_order(void);
-static void change_fields(void);
-static void show_task_info(proc_t *task);
-static void show_procs(void);
-static float get_elapsed_time(void);
-static void show_meminfo(void);
-static void do_stats(proc_t** p, float elapsed_time, int pass);
-static void do_key(char c);
-
-
-/* configurable field display support */
-
-static int pflags[30];
-static int Numfields;
-
-
- /* Name of the config file (in $HOME) */
-#ifndef RCFILE
-#define RCFILE ".toprc"
-#endif
+ /* Determines for whom we're destined, debian or !#@*#! redhat ----- */
+//#define UGH_ITS_4_RH /* use the redhat libproc conventions */
-#ifndef SYS_TOPRC
-#define SYS_TOPRC "/etc/toprc"
-#endif
+ /* Defines intended to be played with ------------------------------ */
+//#define UPCASE_HEXES /* show any hex values in upper case */
+//#define UPCASE_SCALE /* show scaled times & memory upper case */
+//#define UPCASE_SUMMK /* show memory summary kilobytes with 'K' */
-#define MAXLINES 2048
-#define MAXNAMELEN 1024
+//#define COLOR_SLEEPY /* purples and blues (sissy colors) */
+//#define COLOR_CONSOL /* for linux consoles (white on black) */
-/* this is what procps top does by default, so let's do this, if nothing is
- * specified
- */
-#ifndef DEFAULT_SHOW
-/* 0 1 2 3 */
-/* 0123456789012345678901234567890 */
-#define DEFAULT_SHOW "AbcDgHIjklMnoTP|qrsuzyV{EFWX"
-#endif
-static char Fields[256] = "";
+ /* Development/Debugging defines ----------------------------------- */
+//#define TRAK_MAXCAPS /* track & report misc terminfo stuff */
+//#define TRAK_MAXBUFS /* track & report internal buffer usage */
+ /* Special define to test extremely dumb terminals! ---------------- */
+//#define PRETENDNOCAP /* use a terminal without essential caps */
-/* This structure stores some critical information from one frame to
- the next. mostly used for sorting. Added cumulative and resident fields. */
-struct save_hist {
- int ticks;
- int pid;
- int pcpu;
- int utime;
- int stime;
-};
- /* The original terminal attributes. */
-static struct termios Savetty;
- /* The new terminal attributes. */
-static struct termios Rawtty;
- /* Cached termcap entries. */
-static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr;
- /* Current window size. Note that it is legal to set Display_procs
- larger than can fit; if the window is later resized, all will be ok.
- In other words: Display_procs is the specified max number of
- processes to display (zero for infinite), and Maxlines is the actual
- number. */
-static int Lines, Cols, Maxlines, Display_procs;
- /* Maximum length to display of the command line of a process. */
-static unsigned Maxcmd;
-
- /* Controls how long we sleep between screen updates. Accurate to
- microseconds. */
-static float Sleeptime = 5;
- /* for opening/closing the system map */
-static int psdbsucc = 0;
- /* Mode flags. */
-static int Irixmode = 1;
-static int Secure = 0;
-static int Cumulative = 0;
-static int Noidle = 0;
-
-static int CPU_states = 0;
-static char CurrUser[BUFSIZ];
-
-static int CL_pg_shift = (PAGE_SHIFT - 10);
-static int CL_wchan_nout = -1;
-
-static int show_stats = 1; /* show status summary */
-static int show_memory = 1; /* show memory summary */
-static int show_loadav = 1; /* show load average and uptime */
-static int show_cmd = 1; /* show command name instead of commandline */
-
-static pid_t monpids[520]; /* randomly chosen value */
-static const int monpids_max = sizeof(monpids)/sizeof(pid_t);
-static int monpids_index = 0;
-
-static int Loops = -1; /* number of iterations. -1 loops forever */
-static int Batch = 0; /* batch mode. Collect no input, dumb output */
-
-/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */
-enum {
- S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE
-};
-/* default sorting by CPU% */
-static int sort_type = S_PCPU;
-
-/* flags for each possible field. At the moment up to 30 are supported */
-enum {
- P_PID, P_PPID, P_EUID, P_EUSER,
- P_PCPU, P_PMEM, P_TTY, P_PRI,
- P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ,
- P_SIZE, P_TRS, P_SWAP, P_SHARE,
- P_A, P_WP, P_DT, P_RSS,
- P_WCHAN, P_STAT, P_TIME, P_COMMAND,
- P_LCPU, P_FLAGS, P_END
+/*###### Some Typedef's and Enum's #####################################*/
+
+ /* This typedef attempts to ensure consistent 'ticks' handling. */
+typedef unsigned long TICS_t;
+
+ /* This structure consolidates the information that's used
+ in a variety of display roles. */
+typedef struct {
+ const char *head; /* name for column headings + toggle/reorder fields */
+ const char *fmts; /* sprintf format string for field display */
+ const int width; /* field width, if applicable */
+ const int scale; /* scale_num type, if applicable */
+ const int sort; /* sort type, if applicable (used soley by mkcol) */
+ const char *desc; /* description for toggle/reorder fields */
+} FTAB_t;
+
+ /* This structure stores one piece of critical 'history'
+ information from one frame to the next -- we don't calc
+ and save data that goes unused like the old top! */
+typedef struct {
+ int pid;
+ TICS_t tics;
+} HIST_t;
+
+ /* This structure serves single (non-smp), multiple (smp) and
+ consolidated (smp, but Show_cpusum == 1) environments. It is
+ used to calculate a frame's cpu(s) state percentages */
+typedef struct {
+ TICS_t u, /* ticks count as represented in /proc/stat */
+ n, /* (not in the order of our display) */
+ s,
+ i;
+} CPUS_t;
+
+ /* Sorted columns support. */
+typedef int (*QSORT_t)(const void *, const void *);
+enum sort {
+ S_CMD = 'C', S_MEM = 'M', S_TME = 'T', S_PID = 'P', S_TTY = 'Y',
+ S_CPU = 'U', S_USR = 'E'
};
-/* corresponding headers */
-static char *headers[] =
-{
- " PID ", " PPID ", " UID ",
- "USER ", "%CPU ", "%MEM ",
- "TTY ", "PRI ", " NI ",
- "PAGEIN ", "TSIZE ", "DSIZE ",
- " SIZE ", " TRS ", "SWAP ",
- "SHARE ", " A ", " WP ",
- " D ", " RSS ", "WCHAN ",
- "STAT ", " TIME ", "COMMAND",
- "LC ",
- " FLAGS "
+
+ /* The scaling 'type' used with scale_num() -- this is how
+ the passed number is interpreted should scaling be necessary */
+enum scale_num {
+ SK_no, SK_Kb, SK_Mb, SK_Gb
};
-/* corresponding field desciptions */
-static char *headers2[] =
-{
- "Process Id", "Parent Process Id", "User Id",
- "User Name", "CPU Usage", "Memory Usage",
- "Controlling tty", "Priority", "Nice Value",
- "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)",
- "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb",
- "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages",
- "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function",
- "Process Status", "CPU Time", "Command",
- "Last used CPU (expect this to change regularly)",
- "Task Flags (see linux/sched.h)"
+
+ /* Flags for each possible field.
+ At the moment 32 are supported [ see PFlags/PFLAGSSIZ ] */
+enum pflag {
+ P_PID, P_PPID, P_UID, P_USER, P_GROUP, P_TTY,
+ P_PR, P_NI,
+ P_NCPU, P_CPU, P_TIME, P_TIME2,
+ P_MEM, P_VIRT, P_SWAP, P_RES, P_CODE, P_DATA, P_SHR,
+ P_FAULT, P_DIRTY,
+ P_STA, P_CMD, P_WCHAN, P_FLAGS
};
- /* The header printed at the top of the process list.*/
-static char Header[MAXLINES];
-
- /* The response to the interactive 'h' command. */
-#define HELP_SCREEN "\
-Interactive commands are:\n\
-\n\
-space\tUpdate display\n\
-^L\tRedraw the screen\n\
-fF\tadd and remove fields\n\
-oO\tChange order of displayed fields\n\
-h or ?\tPrint this list\n\
-S\tToggle cumulative mode\n\
-i\tToggle display of idle proceses\n\
-I\tToggle between Irix and Solaris views (SMP-only)\n\
-c\tToggle display of command name/line\n\
-l\tToggle display of load average\n\
-m\tToggle display of memory information\n\
-t\tToggle display of summary information\n\
-k\tKill a task (with any signal)\n\
-r\tRenice a task\n\
-N\tSort by pid (Numerically)\n\
-A\tSort by age\n\
-P\tSort by CPU usage\n\
-M\tSort by resident memory usage\n\
-T\tSort by time / cumulative time\n\
-u\tShow only a specific user\n\
-n or #\tSet the number of process to show\n\
-s\tSet the delay in seconds between updates\n\
-W\tWrite configuration file ~/.toprc\n\
-q\tQuit"
-#define SECURE_HELP_SCREEN "\
-Interactive commands available in secure mode are:\n\
-\n\
-space\tUpdate display\n\
-^L\tRedraw the screen\n\
-fF\tadd and remove fields\n\
-h or ?\tPrint this list\n\
-S\tToggle cumulative mode\n\
-i\tToggle display of idle proceses\n\
-c\tToggle display of command name/line\n\
-l\tToggle display of load average\n\
-m\tToggle display of memory information\n\
-t\tToggle display of summary information\n\
-n or #\tSet the number of process to show\n\
-u\tShow only a specific user\n\
-oO\tChange order of displayed fields\n\
-W\tWrite configuration file ~/.toprc\n\
-q\tQuit"
-
- /* Number of lines needed to display the header information. */
-static int header_lines;
-
-/* ############## Some Macro definitions for screen handling ######### */
- /* String to use in error messages. */
-#define PROGNAME "top"
- /* Clear the screen. */
-#define clear_screen() \
- printf("%s", cl)
- /* Show an error in the context of the spiffy full-screen display. */
-#define SHOWMESSAGE(x) do { \
- printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr); \
- printf x; \
- printf ("%s",me); \
- fflush(stdout); \
- sleep(2); \
- } while (0)
+
+/*###### Some Miscellaneous Macro definitions ##########################*/
+
+ /* Message line placement at runtime */
+#define MSG_line ( HSum_lines - 2 )
+
+ /* Used as return argument to achieve normal/reversed sorts
+ in the sort callbacks */
+#define SORT_lt ( Sort_normal ? 1 : -1 )
+#define SORT_gt ( Sort_normal ? -1 : 1 )
+
+ /* Convert some proc stuff into vaules we can actually use */
+#define BYTES_2K(n) (unsigned)( (n) >> 10 )
+#define PAGES_2B(n) (unsigned)( (n) * Page_size )
+#define PAGES_2K(n) BYTES_2K(PAGES_2B(n))
+#define PAGE_CNT(n) (unsigned)( (n) / Page_size )
+
+ /* Yield table size as 'int' */
+#define MAXtbl(t) ( (int)(sizeof(t)/sizeof(t[0])) )
+
+
+/*###### Special Macros (debug and/or informative) #####################*/
+
+ /* Orderly end, with any sort of message - see fmtmk */
+#define debug_END(s) { \
+ static void std_err (const char *); \
+ fputs(Cap_clr_scr, stdout); \
+ std_err(s); \
+ }
+
+#ifdef TRAK_MAXBUFS
+ /* Provide for tracking maximum buffer sizes */
+#define ARPTBUF(f,b) "\n\t%4d of %d\t" #f ", " #b
+#define ASIZBUF(f,b) Siz_ ## f ## _ ## b
+#define AUSEBUF(f,b) Use_ ## f ## _ ## b
+#define BUF2INT(f,b) static int AUSEBUF(f,b) = 0, ASIZBUF(f,b) = 0;
+#define MAXBUFS(f,b) { \
+ if ((int)strlen(b) > AUSEBUF(f,b)) \
+ AUSEBUF(f,b) = (int)strlen(b); \
+ ASIZBUF(f,b) = (int)sizeof(b); \
+ }
+#endif
+
+
+/*###### Some Miscellaneous constants ##################################*/
+
+ /* The default value for the 'k' (kill) request */
+#define DEF_SIGNAL SIGTERM
+
+ /* The default delay twix updates */
+#define DEF_DELAY 2.0
+
+ /* The length of time a 'message' is displayed */
+#define MSG_SLEEP 1
+
+ /* Minimum summary lines (thus, just msg line + col heads) */
+#define SUMMINLINS 2
+
+ /* Specific process id monitoring support (command line only) */
+#define MONPIDMAX 12
+
+ /* Miscellaneous buffer sizes with liberal values
+ -- mostly just to pinpoint source code usage/dependancies */
+#define PFLAGSSIZ 32
+#define CAPBUFSIZ 32
+#define CLRBUFSIZ 64
+#define GETBUFSIZ 32
+#define TNYBUFSIZ 32
+#define SMLBUFSIZ 256
+#define MEDBUFSIZ 512
+#define OURPATHSZ 1024
+#define BIGBUFSIZ 2048
+#define RCFBUFSIZ SMLBUFSIZ
+#define USRNAMSIZ GETBUFSIZ
+#define COLBUFSIZ SMLBUFSIZ + CLRBUFSIZ
+#define ROWBUFSIZ MEDBUFSIZ + CLRBUFSIZ
+
+ /* Color stuff...
+ note: we avoid the use of background color so as to maximize
+ compatibility with the user's xterm settings */
+#ifdef COLOR_SLEEPY
+#define BASEcolor COLOR_MAGENTA
+#define MSGScolor COLOR_CYAN
+#define HEADcolor COLOR_BLUE
+#else
+#ifdef COLOR_CONSOL
+#define BASEcolor COLOR_CYAN
+#define MSGScolor COLOR_RED
+#define HEADcolor COLOR_WHITE
+#else
+#define BASEcolor COLOR_RED
+#define MSGScolor COLOR_RED
+#define HEADcolor COLOR_YELLOW
+#endif
+#endif
+
+
+/*###### Display Support *Data* ########################################*/
+
+ /* The default fields displayed and their order,
+ if nothing is specified by the loser, oops user */
+#define DEF_FIELDS "AbcDefGHiJkLMNOPqrstuVWxy"
+ /* Help screen selectable grouped fields */
+#define JOB_FIELDS "ABWdefikqrstuxyLJMGHVNOPC"
+#define MEM_FIELDS "AMNOPQRSTUWbcdefiklxyVGHJ"
+#define USR_FIELDS "CDEFABWghiknopqrstuxyLJMV"
+
+
+ /* These are the possible fscanf formats used in /proc/stat
+ reads during history processing. */
+#define CPU_FMTS_MULTI "cpu%*d %lu %lu %lu %lu\n"
+#define CPU_FMTS_JUST1 "cpu %lu %lu %lu %lu\n"
+
+
+ /* Summary Lines specially formatted string(s) --
+ see 'show_special' for syntax details + other cautions. */
+#define LOADAV_line "%s -\03%s\n"
+#define STATES_line1 "Tasks:\03" \
+ " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03" \
+ " %3u \02stopped,\03 %3u \02zombie\03\n"
+#define STATES_line2 "%s\03" \
+ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03" \
+ " %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n"
+#ifdef UPCASE_SUMMK
+#define MEMORY_line1 "Mem: \03" \
+ " %8uK \02total,\03 %8uK \02used,\03" \
+ " %8uK \02free,\03 %8uK \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8uK \02total,\03 %8uK \02used,\03" \
+ " %8uK \02free,\03 %8uK \02cached\03\n"
+#else
+#define MEMORY_line1 "Mem: \03" \
+ " %8uk \02total,\03 %8uk \02used,\03" \
+ " %8uk \02free,\03 %8uk \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8uk \02total,\03 %8uk \02used,\03" \
+ " %8uk \02free,\03 %8uk \02cached\03\n"
+#endif
+
+
+ /* Colors Help specially formatted string(s) --
+ see 'show_special' for syntax details + other cautions. */
+#define COLOR_sample \
+ "%s%s's \01Help for color mapping\02 - %s\n" \
+ "\n" \
+ " color -\03 08:41:24 up 1 day, 6:07, 7 users, load average:\n" \
+ " Tasks:\03 64 \02total,\03 2 \02running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \
+ " State cpu0 :\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03\n" \
+ " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \
+ " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \
+ " 17284 \03 pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bas\03\n" \
+ " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R< color -b\07\n" \
+ " 11005 \03 ? \07 9 0 0.0 0:02.50 2852 1008 S amor -se\03\n" \
+ " 2924 \03 ? \07 9 -1 0.0 1:08.16 30040 20m S< X :0\03\n" \
+ " (normal toggles available: \01b\02 = bold/reverse; \01z\02 = color/mono)\n" \
+ "\n\n" \
+ "Select \01target\02 as upper case letter:\n" \
+ " B\02 = Base color,\01 H\02 = Column Headings,\01 M\02 = Messages/Prompts\n" \
+ "Select \01color\02 as number:\n" \
+ " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \
+ " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \
+ "\n" \
+ "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \
+ " press 'q' to abort or <Enter> to commit " \
+ ""
+
+
+ /* Keyboard Help specially formatted string(s) --
+ see 'show_special' for syntax details + other cautions. */
+#define HELP_data \
+ "%s's \01Help for interactive commands\02 - %s\n" \
+ "status: \01Secure mode \03%s\02;\01 Cumulative mode \03%s\02; \01Delay time \03%.1f\02 seconds\n" \
+ " sp or ^L Redraw screen\n" \
+ " o or O Rearrange fields\n" \
+ " f or F Add and remove fields, or select group now from:\n" \
+ " \01j\02) job fields; \01m\02) memory fields; \01u\02) user fields\n" \
+ " Z Change color mapping\05\n" \
+ "\n" \
+ "C,M,P,T,U,Y,E Sort: \01C\02) cmd; \01M\02) mem; \01P\02) pid; \01T\02) time; \01U\02) cpu; \01Y\02) tty; \01E\02) user\n" \
+ " R Toggle normal/reverse sort for any of above\n" \
+ " l,t,m Toggle summary: \01l\02) load avg; \01t\02) task/cpu stats; \01m\02) mem info\n" \
+ " c,i,S Toggle: \01c\02) cmd name/line; \01i\02) idle tasks; \01S\02) cumulative time\n" \
+ " x,y\05 Toggle highlights: \01x\02) sort field; \01y\02) running tasks\n" \
+ " z,b\05 Toggle: \01z\02) color/mono; \01b\02) bold/reverse if 'x' or 'y'\n" \
+ " 1,I SMP views:\01 1\02) single/separate states; \01I\02) Irix/Solaris mode\n" \
+ "\n" \
+ "%s" \
+ " u Show specific user only\n" \
+ " # or n Set maximum tasks displayed\n" \
+ " W Write configuration file\n" \
+ " q Quit\n" \
+ "Press any key to continue " \
+ ""
+
+ /* This guy goes above the 'u' help text (maybe) */
+#define HELP_unsecured \
+ " k Kill a task\n" \
+ " r Renice a task\n" \
+ " s or d Set update interval\n" \
+ ""
+
+
+ /* Fields Reorder/Toggle specially formatted string(s) --
+ see 'show_special' for syntax details + other cautions
+ note: the leading newline below serves really dumb terminals;
+ if there's no 'cursor_home', the screen will be a mess
+ but this part will still be functional. */
+#define FIELDS_current \
+ "\n%s%s's\01 Current Fields\02: \01 %s \04\n%s " \
+ ""
+
+ /* Some extra explanatory text which accompanies the Fields display.
+ note: the newlines cannot actually be used, they just serve as
+ substring delimiters for the 'display_fields' routine. */
+#define FIELDS_xtra \
+ "Flags field:\n" \
+ " 0x00000001 PF_ALIGNWARN\n" \
+ " 0x00000002 PF_STARTING\n" \
+ " 0x00000004 PF_EXITING\n" \
+ " 0x00000040 PF_FORKNOEXEC\n" \
+ " 0x00000100 PF_SUPERPRIV\n" \
+ " 0x00000200 PF_DUMPCORE\n" \
+ " 0x00000400 PF_SIGNALED\n" \
+ " 0x00000800 PF_MEMALLOC\n" \
+ " 0x00040000 PF_KERNTHREAD (2.5)\n" \
+ " 0x00100000 PF_USEDFPU (thru 2.4)\n" \
+ " 0x00400000 PF_ATOMICALLOC\n" \
+ "\n" \
+ "Memory notes:\n" \
+ " VIRT = SWAP + RES\n" \
+ " RES = CODE + DATA\n" \
+ ""
+\f
+/*###### Some Prototypes (ha!) #########################################*/
+
+ /* None of these are necessary when the source file is properly
+ * organized -- they're here for documentation purposes !
+ * Note also that functions are alphabetical within a group to aid
+ * source code navigation, which often influences choice of identifers. */
+/*------ Sort callbacks ------------------------------------------------*/
+//atic int sort_cmd (proc_t **P, proc_t **Q);
+//atic int sort_cpu (proc_t **P, proc_t **Q);
+//atic int sort_mem (proc_t **P, proc_t **Q);
+//atic int sort_pid (proc_t **P, proc_t **Q);
+//atic int sort_tme (proc_t **P, proc_t **Q);
+//atic int sort_tty (proc_t **P, proc_t **Q);
+//atic int sort_usr (proc_t **P, proc_t **Q);
+/*------ Tiny useful routine(s) ----------------------------------------*/
+//atic int chin (int ech, char *buf, unsigned cnt);
+//atic const char *fmtmk (const char *fmts, ...);
+//atic char *strim (int sp, char *str);
+//atic char *tg2 (int x, int y);
+/*------ Misc Color/Highlighting support -------------------------------*/
+//atic void capsmk (void);
+//atic void msg_save (const char *fmts, ...);
+//atic void show_msg (const char *str);
+//atic void show_pmt (const char *str);
+//atic void show_special (const char *glob);
+//atic void tweak_colors (void);
+/*------ Small utility routines ----------------------------------------*/
+//atic char *ask_str (const char *prompt);
+//atic float get_float (const char *prompt);
+//atic int get_int (const char *prompt);
+//atic void mkheadings (void);
+//atic char *scale_num (unsigned num, const unsigned width, const unsigned type);
+//atic char *scale_tics (TICS_t tics, const unsigned width);
+//atic float time_elapsed (void);
+/*------ Exit and Signal handled routines ------------------------------*/
+//atic void bye_bye (int eno, const char *str);
+//atic void std_err (const char *str);
+//atic void stop (int dont_care_sig);
+//atic void suspend (int dont_care_sig);
+//atic void window_resize (int dont_care_sig);
+/*------ Startup routines ----------------------------------------------*/
+//atic void parse_argvs (char **argv);
+//atic void parse_rc (char *opts);
+//atic void rcfiles_read (void);
+//atic void terminal_set (void);
+/*------ Field Selection/Ordering routines -----------------------------*/
+//atic void display_fields (void);
+//atic void fields_reorder (void);
+//atic void fields_toggle (void);
+/*------ Library Alternatives ------------------------------------------*/
+//atic void *alloc_c (unsigned numb);
+//atic void *alloc_r (void *q, unsigned numb);
+//atic proc_t **readprocs (proc_t **tbl);
+/*------ Main screen routines ------------------------------------------*/
+//atic void do_key (unsigned c);
+#ifdef UGH_ITS_4_RH
+//atic unsigned frame_memory (void);
+#else
+//atic void frame_memory (void);
+#endif
+//atic void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx);
+//atic void frame_states (proc_t **p, int show);
+//atic void mkcol (unsigned idx, int sta, int *pad, char *buf, ...);
+#ifdef UGH_ITS_4_RH
+//atic void show_a_task (proc_t *task, unsigned mempgs);
+#else
+//atic void show_a_task (proc_t *task);
+#endif
+//atic void show_everything (void);
+/*------ Entry point ---------------------------------------------------*/
+// int main (int dont_care_argc, char **argv);
+
+
+ /* just sanity check(s)... */
+#if USRNAMSIZ < GETBUFSIZ
+ #error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !"
+#endif
+
+#endif /* _Itop */
+\f