
Due to differences in (mainly) measuring and accumulating CPU times, the two top programs end up serving different purposes: the NetBSD top is a system administration tool, while the MINIX3 top (now mtop) is a performance debugging tool. Therefore, we keep both. The newly imported BSD top has a few MINIX3-specific changes. CPU statistics separate system time from kernel time, rather than kernel time from time spent on handling interrupts. Memory statistics show numbers that are currently relevant for MINIX3. Swap statistics are disabled entirely. All of these changes effectively bring it closer to how mtop already worked as well. Change-Id: I9611917cb03e164ddf012c5def6da0e7fede826d
997 lines
22 KiB
C
997 lines
22 KiB
C
/*
|
|
* Copyright (c) 1984 through 2008, William LeFebvre
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* * Neither the name of William LeFebvre nor the names of other
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
const char *copyright =
|
|
"Copyright (c) 1984 through 2008, William LeFebvre";
|
|
|
|
/*
|
|
* Changes to other files that we can do at the same time:
|
|
* screen.c:init_termcap: get rid of the "interactive" argument and have it
|
|
* pass back something meaningful (such as success/failure/error).
|
|
*/
|
|
|
|
#include "os.h"
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef HAVE_SYS_UTSNAME_H
|
|
#include <sys/utsname.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
/* definitions */
|
|
#ifndef STDIN_FILENO
|
|
#define STDIN_FILENO 0
|
|
#endif
|
|
|
|
/* determine which type of signal functions to use */
|
|
/* cant have sigaction without sigprocmask */
|
|
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
|
|
#undef HAVE_SIGACTION
|
|
#endif
|
|
/* always use sigaction when it is available */
|
|
#ifdef HAVE_SIGACTION
|
|
#undef HAVE_SIGHOLD
|
|
#else
|
|
/* use sighold/sigrelse, otherwise use old fashioned BSD signals */
|
|
#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
|
|
#define BSD_SIGNALS
|
|
#endif
|
|
#endif
|
|
|
|
/* if FD_SET and friends aren't present, then fake something up */
|
|
#ifndef FD_SET
|
|
typedef int fd_set;
|
|
#define FD_ZERO(x) (*(x) = 0)
|
|
#define FD_SET(f, x) (*(x) = 1<<f)
|
|
#endif
|
|
|
|
/* includes specific to top */
|
|
|
|
#include "top.h"
|
|
#include "machine.h"
|
|
#include "globalstate.h"
|
|
#include "commands.h"
|
|
#include "display.h"
|
|
#include "screen.h"
|
|
#include "boolean.h"
|
|
#include "username.h"
|
|
#include "utils.h"
|
|
#include "version.h"
|
|
#ifdef ENABLE_COLOR
|
|
#include "color.h"
|
|
#endif
|
|
|
|
/* definitions */
|
|
#define BUFFERSIZE 4096
|
|
#define JMP_RESUME 1
|
|
#define JMP_RESIZE 2
|
|
|
|
/* externs for getopt: */
|
|
extern int optind;
|
|
extern char *optarg;
|
|
|
|
/* statics */
|
|
static char stdoutbuf[BUFFERSIZE];
|
|
static jmp_buf jmp_int;
|
|
|
|
/* globals */
|
|
char *myname;
|
|
|
|
void
|
|
quit(int status)
|
|
|
|
{
|
|
screen_end();
|
|
chdir("/tmp");
|
|
exit(status);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* signal handlers
|
|
*/
|
|
|
|
static void
|
|
set_signal(int sig, RETSIGTYPE (*handler)(int))
|
|
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
struct sigaction action;
|
|
|
|
action.sa_handler = handler;
|
|
action.sa_flags = 0;
|
|
(void) sigaction(sig, &action, NULL);
|
|
#else
|
|
(void) signal(sig, handler);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
release_signal(int sig)
|
|
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
sigset_t set;
|
|
sigemptyset(&set);
|
|
sigaddset(&set, sig);
|
|
sigprocmask(SIG_UNBLOCK, &set, NULL);
|
|
#endif
|
|
|
|
#ifdef HAVE_SIGHOLD
|
|
sigrelse(sig);
|
|
#endif
|
|
|
|
#ifdef BSD_SIGNALS
|
|
(void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
|
|
#endif
|
|
}
|
|
|
|
static RETSIGTYPE
|
|
sig_leave(int i) /* exit under normal conditions -- INT handler */
|
|
|
|
{
|
|
screen_end();
|
|
exit(EX_OK);
|
|
}
|
|
|
|
static RETSIGTYPE
|
|
sig_tstop(int i) /* SIGTSTP handler */
|
|
|
|
{
|
|
/* move to the lower left */
|
|
screen_end();
|
|
fflush(stdout);
|
|
|
|
/* default the signal handler action */
|
|
set_signal(SIGTSTP, SIG_DFL);
|
|
|
|
/* unblock the TSTP signal */
|
|
release_signal(SIGTSTP);
|
|
|
|
/* send ourselves a TSTP to stop the process */
|
|
(void) kill(0, SIGTSTP);
|
|
|
|
/* reset the signal handler */
|
|
set_signal(SIGTSTP, sig_tstop);
|
|
|
|
/* reinit screen */
|
|
screen_reinit();
|
|
|
|
/* jump back to a known place in the main loop */
|
|
longjmp(jmp_int, JMP_RESUME);
|
|
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
#ifdef SIGWINCH
|
|
static RETSIGTYPE
|
|
sig_winch(int i) /* SIGWINCH handler */
|
|
|
|
{
|
|
/* reascertain the screen dimensions */
|
|
screen_getsize();
|
|
|
|
/* jump back to a known place in the main loop */
|
|
longjmp(jmp_int, JMP_RESIZE);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
static sigset_t signalset;
|
|
#endif
|
|
|
|
static void *
|
|
hold_signals(void)
|
|
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
sigemptyset(&signalset);
|
|
sigaddset(&signalset, SIGINT);
|
|
sigaddset(&signalset, SIGQUIT);
|
|
sigaddset(&signalset, SIGTSTP);
|
|
#ifdef SIGWINCH
|
|
sigaddset(&signalset, SIGWINCH);
|
|
#endif
|
|
sigprocmask(SIG_BLOCK, &signalset, NULL);
|
|
return (void *)(&signalset);
|
|
#endif
|
|
|
|
#ifdef HAVE_SIGHOLD
|
|
sighold(SIGINT);
|
|
sighold(SIGQUIT);
|
|
sighold(SIGTSTP);
|
|
#ifdef SIGWINCH
|
|
sighold(SIGWINCH);
|
|
return NULL;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BSD_SIGNALS
|
|
int mask;
|
|
#ifdef SIGWINCH
|
|
mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
|
|
sigmask(SIGTSTP) | sigmask(SIGWINCH));
|
|
#else
|
|
mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
|
|
return (void *)mask;
|
|
#endif
|
|
#endif
|
|
|
|
}
|
|
|
|
static void
|
|
set_signals(void)
|
|
|
|
{
|
|
(void) set_signal(SIGINT, sig_leave);
|
|
(void) set_signal(SIGQUIT, sig_leave);
|
|
(void) set_signal(SIGTSTP, sig_tstop);
|
|
#ifdef SIGWINCH
|
|
(void) set_signal(SIGWINCH, sig_winch);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
release_signals(void *parm)
|
|
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
|
|
#endif
|
|
|
|
#ifdef HAVE_SIGHOLD
|
|
sigrelse(SIGINT);
|
|
sigrelse(SIGQUIT);
|
|
sigrelse(SIGTSTP);
|
|
#ifdef SIGWINCH
|
|
sigrelse(SIGWINCH);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BSD_SIGNALS
|
|
(void) sigsetmask((int)parm);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* void do_arguments(globalstate *gstate, int ac, char **av)
|
|
*
|
|
* Arguments processing. gstate points to the global state,
|
|
* ac and av are the arguments to process. This can be called
|
|
* multiple times with different sets of arguments.
|
|
*/
|
|
|
|
#ifdef HAVE_GETOPT_LONG
|
|
static struct option longopts[] = {
|
|
{ "percpustates", no_argument, NULL, '1' },
|
|
{ "color", no_argument, NULL, 'C' },
|
|
{ "debug", no_argument, NULL, 'D' },
|
|
{ "system-procs", no_argument, NULL, 'S' },
|
|
{ "idle-procs", no_argument, NULL, 'I' },
|
|
{ "tag-names", no_argument, NULL, 'T' },
|
|
{ "all", no_argument, NULL, 'a' },
|
|
{ "batch", no_argument, NULL, 'b' },
|
|
{ "full-commands", no_argument, NULL, 'c' },
|
|
{ "interactive", no_argument, NULL, 'i' },
|
|
{ "quick", no_argument, NULL, 'q' },
|
|
{ "threads", no_argument, NULL, 't' },
|
|
{ "uids", no_argument, NULL, 'u' },
|
|
{ "version", no_argument, NULL, 'v' },
|
|
{ "delay", required_argument, NULL, 's' },
|
|
{ "displays", required_argument, NULL, 'd' },
|
|
{ "user", required_argument, NULL, 'U' },
|
|
{ "sort-order", required_argument, NULL, 'o' },
|
|
{ "pid", required_argument, NULL, 'p' },
|
|
{ "display-mode", required_argument, NULL, 'm' },
|
|
{ NULL, 0, NULL, 0 },
|
|
};
|
|
#endif
|
|
|
|
|
|
static void
|
|
do_arguments(globalstate *gstate, int ac, char **av)
|
|
|
|
{
|
|
int i;
|
|
double f;
|
|
|
|
/* this appears to keep getopt happy */
|
|
optind = 1;
|
|
|
|
#ifdef HAVE_GETOPT_LONG
|
|
while ((i = getopt_long(ac, av, "1CDSITabcinp:qtuvs:d:U:o:m:", longopts, NULL)) != -1)
|
|
#else
|
|
while ((i = getopt(ac, av, "1CDSITabcinp:qtuvs:d:U:o:m:")) != EOF)
|
|
#endif
|
|
{
|
|
switch(i)
|
|
{
|
|
case '1':
|
|
gstate->percpustates = !gstate->percpustates;
|
|
break;
|
|
#ifdef ENABLE_COLOR
|
|
case 'C':
|
|
gstate->use_color = !gstate->use_color;
|
|
break;
|
|
#endif
|
|
|
|
case 'D':
|
|
debug_set(1);
|
|
break;
|
|
|
|
case 'v':
|
|
fprintf(stderr, "%s: version %s\n", myname, version_string());
|
|
exit(EX_OK);
|
|
break;
|
|
|
|
case 'b':
|
|
case 'n':
|
|
gstate->interactive = No;
|
|
break;
|
|
|
|
case 'a':
|
|
gstate->displays = Infinity;
|
|
gstate->topn = Infinity;
|
|
break;
|
|
|
|
case 'i':
|
|
gstate->interactive = Yes;
|
|
break;
|
|
|
|
case 'o':
|
|
gstate->order_name = optarg;
|
|
break;
|
|
|
|
case 'd':
|
|
i = atoiwi(optarg);
|
|
if (i == Invalid || i == 0)
|
|
{
|
|
message_error(" Bad display count");
|
|
}
|
|
else
|
|
{
|
|
gstate->displays = i;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
f = atof(optarg);
|
|
if (f < 0 || (f == 0 && getuid() != 0))
|
|
{
|
|
message_error(" Bad seconds delay");
|
|
}
|
|
else
|
|
{
|
|
gstate->delay = f;
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
gstate->show_usernames = !gstate->show_usernames;
|
|
break;
|
|
|
|
case 'U':
|
|
i = userid(optarg);
|
|
if (i == -1)
|
|
{
|
|
message_error(" Unknown user '%s'", optarg);
|
|
}
|
|
else
|
|
{
|
|
gstate->pselect.uid = i;
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
i = atoi(optarg);
|
|
gstate->pselect.mode = i;
|
|
break;
|
|
|
|
case 'S':
|
|
gstate->pselect.system = !gstate->pselect.system;
|
|
break;
|
|
|
|
case 'I':
|
|
gstate->pselect.idle = !gstate->pselect.idle;
|
|
break;
|
|
|
|
#ifdef ENABLE_COLOR
|
|
case 'T':
|
|
gstate->show_tags = 1;
|
|
break;
|
|
#endif
|
|
|
|
case 'c':
|
|
gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
|
|
break;
|
|
|
|
case 't':
|
|
gstate->pselect.threads = !gstate->pselect.threads;
|
|
break;
|
|
|
|
case 'p':
|
|
gstate->pselect.pid = atoi(optarg);
|
|
break;
|
|
|
|
case 'q': /* be quick about it */
|
|
/* only allow this if user is really root */
|
|
if (getuid() == 0)
|
|
{
|
|
/* be very un-nice! */
|
|
(void) nice(-20);
|
|
}
|
|
else
|
|
{
|
|
message_error(" Option -q can only be used by root");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "\
|
|
Top version %s\n\
|
|
Usage: %s [-1CISTabcinqtuv] [-d count] [-m mode] [-o field] [-p pid]\n\
|
|
[-s time] [-U username] [number]\n",
|
|
version_string(), myname);
|
|
exit(EX_USAGE);
|
|
}
|
|
}
|
|
|
|
/* get count of top processes to display */
|
|
if (optind < ac && *av[optind])
|
|
{
|
|
if ((i = atoiwi(av[optind])) == Invalid)
|
|
{
|
|
message_error(" Process count not a number");
|
|
}
|
|
else
|
|
{
|
|
gstate->topn = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_display(globalstate *gstate)
|
|
|
|
{
|
|
int active_procs;
|
|
int i;
|
|
time_t curr_time;
|
|
caddr_t processes;
|
|
struct system_info system_info;
|
|
char *hdr;
|
|
|
|
/* get the time */
|
|
time_mark(&(gstate->now));
|
|
curr_time = (time_t)(gstate->now.tv_sec);
|
|
|
|
/* get the current stats */
|
|
get_system_info(&system_info);
|
|
|
|
/* get the current processes */
|
|
processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
|
|
|
|
/* determine number of processes to actually display */
|
|
if (gstate->topn > 0)
|
|
{
|
|
/* this number will be the smallest of: active processes,
|
|
number user requested, number current screen accomodates */
|
|
active_procs = system_info.P_ACTIVE;
|
|
if (active_procs > gstate->topn)
|
|
{
|
|
active_procs = gstate->topn;
|
|
}
|
|
if (active_procs > gstate->max_topn)
|
|
{
|
|
active_procs = gstate->max_topn;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* dont show any */
|
|
active_procs = 0;
|
|
}
|
|
|
|
#ifdef HAVE_FORMAT_PROCESS_HEADER
|
|
/* get the process header to use */
|
|
hdr = format_process_header(&(gstate->pselect), processes, active_procs);
|
|
#else
|
|
hdr = gstate->header_text;
|
|
#endif
|
|
|
|
/* full screen or update? */
|
|
if (gstate->fulldraw)
|
|
{
|
|
display_clear();
|
|
i_loadave(system_info.last_pid, system_info.load_avg);
|
|
i_uptime(&(gstate->statics->boottime), &curr_time);
|
|
i_timeofday(&curr_time);
|
|
i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
|
|
if (gstate->show_cpustates)
|
|
{
|
|
i_cpustates(system_info.cpustates);
|
|
}
|
|
else
|
|
{
|
|
if (smart_terminal)
|
|
{
|
|
z_cpustates();
|
|
}
|
|
gstate->show_cpustates = Yes;
|
|
}
|
|
i_kernel(system_info.kernel);
|
|
i_memory(system_info.memory);
|
|
i_swap(system_info.swap);
|
|
i_message(&(gstate->now));
|
|
i_header(hdr);
|
|
for (i = 0; i < active_procs; i++)
|
|
{
|
|
i_process(i, format_next_process(processes, gstate->get_userid));
|
|
}
|
|
i_endscreen();
|
|
if (gstate->smart_terminal)
|
|
{
|
|
gstate->fulldraw = No;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u_loadave(system_info.last_pid, system_info.load_avg);
|
|
u_uptime(&(gstate->statics->boottime), &curr_time);
|
|
i_timeofday(&curr_time);
|
|
u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
|
|
u_cpustates(system_info.cpustates);
|
|
u_kernel(system_info.kernel);
|
|
u_memory(system_info.memory);
|
|
u_swap(system_info.swap);
|
|
u_message(&(gstate->now));
|
|
u_header(hdr);
|
|
for (i = 0; i < active_procs; i++)
|
|
{
|
|
u_process(i, format_next_process(processes, gstate->get_userid));
|
|
}
|
|
u_endscreen();
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
timeval_xdprint(char *s, struct timeval tv)
|
|
|
|
{
|
|
xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
do_wait(globalstate *gstate)
|
|
|
|
{
|
|
struct timeval wait;
|
|
|
|
double2tv(&wait, gstate->delay);
|
|
select(0, NULL, NULL, NULL, &wait);
|
|
}
|
|
|
|
static void
|
|
do_command(globalstate *gstate)
|
|
|
|
{
|
|
int status;
|
|
struct timeval wait = {0, 0};
|
|
struct timeval now;
|
|
fd_set readfds;
|
|
unsigned char ch;
|
|
|
|
/* calculate new refresh time */
|
|
gstate->refresh = gstate->now;
|
|
double2tv(&now, gstate->delay);
|
|
timeradd(&now, &gstate->refresh, &gstate->refresh);
|
|
time_get(&now);
|
|
|
|
/* loop waiting for time to expire */
|
|
do {
|
|
/* calculate time to wait */
|
|
if (gstate->delay > 0)
|
|
{
|
|
wait = gstate->refresh;
|
|
wait.tv_usec -= now.tv_usec;
|
|
if (wait.tv_usec < 0)
|
|
{
|
|
wait.tv_usec += 1000000;
|
|
wait.tv_sec--;
|
|
}
|
|
wait.tv_sec -= now.tv_sec;
|
|
}
|
|
|
|
/* set up arguments for select on stdin (0) */
|
|
FD_ZERO(&readfds);
|
|
FD_SET(STDIN_FILENO, &readfds);
|
|
|
|
/* wait for something to read or time out */
|
|
if (select(32, &readfds, NULL, NULL, &wait) > 0)
|
|
{
|
|
/* read it */
|
|
if (read(STDIN_FILENO, &ch, 1) != 1)
|
|
{
|
|
/* read error */
|
|
message_error(" Read error on stdin");
|
|
quit(EX_DATAERR);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/* mark pending messages as old */
|
|
message_mark();
|
|
|
|
/* dispatch */
|
|
status = command_process(gstate, (int)ch);
|
|
switch(status)
|
|
{
|
|
case CMD_ERROR:
|
|
quit(EX_SOFTWARE);
|
|
/*NOTREACHED*/
|
|
|
|
case CMD_REFRESH:
|
|
return;
|
|
|
|
case CMD_UNKNOWN:
|
|
message_error(" Unknown command");
|
|
break;
|
|
|
|
case CMD_NA:
|
|
message_error(" Command not available");
|
|
}
|
|
}
|
|
|
|
/* get new time */
|
|
time_get(&now);
|
|
} while (timercmp(&now, &(gstate->refresh), < ));
|
|
}
|
|
|
|
static void
|
|
do_minidisplay(globalstate *gstate)
|
|
|
|
{
|
|
double real_delay;
|
|
struct system_info si;
|
|
|
|
/* save the real delay and substitute 1 second */
|
|
real_delay = gstate->delay;
|
|
gstate->delay = 1;
|
|
|
|
/* wait 1 second for a command */
|
|
time_mark(&(gstate->now));
|
|
do_command(gstate);
|
|
|
|
/* do a mini update that only updates the cpustates */
|
|
get_system_info(&si);
|
|
u_cpustates(si.cpustates);
|
|
|
|
/* restore the delay time */
|
|
gstate->delay = real_delay;
|
|
|
|
/* done */
|
|
i_endscreen();
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
char *env_top;
|
|
char **preset_argv;
|
|
int preset_argc = 0;
|
|
void *mask;
|
|
volatile int need_mini = 1;
|
|
static char top[] = "top";
|
|
|
|
struct statics statics;
|
|
globalstate *gstate;
|
|
|
|
/* get our name */
|
|
if (argc > 0)
|
|
{
|
|
if ((myname = strrchr(argv[0], '/')) == 0)
|
|
{
|
|
myname = argv[0];
|
|
}
|
|
else
|
|
{
|
|
myname++;
|
|
}
|
|
} else
|
|
myname = top;
|
|
|
|
|
|
/* binary compatibility check */
|
|
#ifdef HAVE_UNAME
|
|
{
|
|
struct utsname uts;
|
|
|
|
if (uname(&uts) == 0)
|
|
{
|
|
if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
|
|
{
|
|
fprintf(stderr, "%s: incompatible hardware platform\n",
|
|
myname);
|
|
exit(EX_UNAVAILABLE);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* initialization */
|
|
gstate = ecalloc(1, sizeof(globalstate));
|
|
gstate->statics = &statics;
|
|
time_mark(NULL);
|
|
|
|
/* preset defaults for various options */
|
|
gstate->show_usernames = Yes;
|
|
gstate->topn = DEFAULT_TOPN;
|
|
gstate->delay = DEFAULT_DELAY;
|
|
gstate->fulldraw = Yes;
|
|
gstate->use_color = Yes;
|
|
gstate->interactive = Maybe;
|
|
gstate->percpustates = No;
|
|
|
|
/* preset defaults for process selection */
|
|
gstate->pselect.idle = Yes;
|
|
gstate->pselect.system = Yes;
|
|
gstate->pselect.fullcmd = No;
|
|
gstate->pselect.command = NULL;
|
|
gstate->pselect.uid = -1;
|
|
gstate->pselect.pid = -1;
|
|
gstate->pselect.mode = 0;
|
|
|
|
/* use a large buffer for stdout */
|
|
#ifdef HAVE_SETVBUF
|
|
setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
|
|
#else
|
|
#ifdef HAVE_SETBUFFER
|
|
setbuffer(stdout, stdoutbuf, BUFFERSIZE);
|
|
#endif
|
|
#endif
|
|
|
|
/* get preset options from the environment */
|
|
if ((env_top = getenv("TOP")) != NULL)
|
|
{
|
|
preset_argv = argparse(env_top, &preset_argc);
|
|
preset_argv[0] = myname;
|
|
do_arguments(gstate, preset_argc, preset_argv);
|
|
}
|
|
|
|
/* process arguments */
|
|
do_arguments(gstate, argc, argv);
|
|
|
|
#ifdef ENABLE_COLOR
|
|
/* If colour has been turned on read in the settings. */
|
|
env_top = getenv("TOPCOLOURS");
|
|
if (!env_top)
|
|
{
|
|
env_top = getenv("TOPCOLORS");
|
|
}
|
|
/* must do something about error messages */
|
|
color_env_parse(env_top);
|
|
color_activate(gstate->use_color);
|
|
#endif
|
|
|
|
/* in order to support forward compatability, we have to ensure that
|
|
the entire statics structure is set to a known value before we call
|
|
machine_init. This way fields that a module does not know about
|
|
will retain their default values */
|
|
memzero((void *)&statics, sizeof(statics));
|
|
statics.boottime = -1;
|
|
|
|
/* call the platform-specific init */
|
|
if (machine_init(&statics) == -1)
|
|
{
|
|
exit(EX_SOFTWARE);
|
|
}
|
|
|
|
/* create a helper list of sort order names */
|
|
gstate->order_namelist = string_list(statics.order_names);
|
|
|
|
/* look up chosen sorting order */
|
|
if (gstate->order_name != NULL)
|
|
{
|
|
int i;
|
|
|
|
if (statics.order_names == NULL)
|
|
{
|
|
message_error(" This platform does not support arbitrary ordering");
|
|
}
|
|
else if ((i = string_index(gstate->order_name,
|
|
statics.order_names)) == -1)
|
|
{
|
|
message_error(" Sort order `%s' not recognized", gstate->order_name);
|
|
message_error(" Recognized sort orders: %s", gstate->order_namelist);
|
|
}
|
|
else
|
|
{
|
|
gstate->order_index = i;
|
|
}
|
|
}
|
|
|
|
/* initialize extensions */
|
|
init_username();
|
|
|
|
/* initialize termcap */
|
|
gstate->smart_terminal = screen_readtermcap(gstate->interactive);
|
|
|
|
/* determine interactive state */
|
|
if (gstate->interactive == Maybe)
|
|
{
|
|
gstate->interactive = smart_terminal;
|
|
}
|
|
|
|
/* if displays were not specified, choose an appropriate default */
|
|
if (gstate->displays == 0)
|
|
{
|
|
gstate->displays = gstate->smart_terminal ? Infinity: 1;
|
|
}
|
|
|
|
/* we don't need a mini display when delay is less than 2
|
|
seconds or when we are not on a smart terminal */
|
|
if (gstate->delay <= 1 || !smart_terminal)
|
|
{
|
|
need_mini = 0;
|
|
}
|
|
|
|
#ifndef HAVE_FORMAT_PROCESS_HEADER
|
|
/* set constants for username/uid display */
|
|
if (gstate->show_usernames)
|
|
{
|
|
gstate->header_text = format_header("USERNAME");
|
|
gstate->get_userid = username;
|
|
}
|
|
else
|
|
{
|
|
gstate->header_text = format_header(" UID ");
|
|
gstate->get_userid = itoa7;
|
|
}
|
|
#endif
|
|
gstate->pselect.usernames = gstate->show_usernames;
|
|
|
|
/* initialize display */
|
|
if ((gstate->max_topn = display_init(&statics, gstate->percpustates)) == -1)
|
|
{
|
|
fprintf(stderr, "%s: display too small\n", myname);
|
|
exit(EX_OSERR);
|
|
}
|
|
|
|
/* check for infinity and for overflowed screen */
|
|
if (gstate->topn == Infinity)
|
|
{
|
|
gstate->topn = INT_MAX;
|
|
}
|
|
else if (gstate->topn > gstate->max_topn)
|
|
{
|
|
message_error(" This terminal can only display %d processes",
|
|
gstate->max_topn);
|
|
}
|
|
|
|
#ifdef ENABLE_COLOR
|
|
/* producing a list of color tags is easy */
|
|
if (gstate->show_tags)
|
|
{
|
|
color_dump(stdout);
|
|
exit(EX_OK);
|
|
}
|
|
#endif
|
|
|
|
/* hold all signals while we initialize the screen */
|
|
mask = hold_signals();
|
|
screen_init();
|
|
|
|
/* set the signal handlers */
|
|
set_signals();
|
|
|
|
/* longjmp re-entry point */
|
|
/* set the jump buffer for long jumps out of signal handlers */
|
|
if (setjmp(jmp_int) != 0)
|
|
{
|
|
/* this is where we end up after processing sigwinch or sigtstp */
|
|
|
|
/* tell display to resize its buffers, and get the new length */
|
|
if ((gstate->max_topn = display_resize()) == -1)
|
|
{
|
|
/* thats bad */
|
|
quit(EX_OSERR);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/* set up for a full redraw, and get the current line count */
|
|
gstate->fulldraw = Yes;
|
|
|
|
/* safe to release the signals now */
|
|
release_signals(mask);
|
|
}
|
|
else
|
|
{
|
|
/* release the signals */
|
|
release_signals(mask);
|
|
|
|
/* some systems require a warmup */
|
|
/* always do a warmup for batch mode */
|
|
if (gstate->interactive == 0 || statics.flags.warmup)
|
|
{
|
|
struct system_info system_info;
|
|
struct timeval timeout;
|
|
|
|
time_mark(&(gstate->now));
|
|
get_system_info(&system_info);
|
|
(void)get_process_info(&system_info, &gstate->pselect, 0);
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
select(0, NULL, NULL, NULL, &timeout);
|
|
|
|
/* if we've warmed up, then we can show good states too */
|
|
gstate->show_cpustates = Yes;
|
|
need_mini = 0;
|
|
}
|
|
}
|
|
|
|
/* main loop */
|
|
while ((gstate->displays == -1) || (--gstate->displays > 0))
|
|
{
|
|
do_display(gstate);
|
|
if (gstate->interactive)
|
|
{
|
|
if (need_mini)
|
|
{
|
|
do_minidisplay(gstate);
|
|
need_mini = 0;
|
|
}
|
|
do_command(gstate);
|
|
}
|
|
else
|
|
{
|
|
do_wait(gstate);
|
|
}
|
|
}
|
|
|
|
/* do one last display */
|
|
do_display(gstate);
|
|
|
|
quit(EX_OK);
|
|
/* NOTREACHED */
|
|
return 1; /* Keep compiler quiet. */
|
|
}
|