char *copyright =
"Copyright (c) 1984 through 2008, William LeFebvre";
#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
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
#undef HAVE_SIGACTION
#endif
#ifdef HAVE_SIGACTION
#undef HAVE_SIGHOLD
#else
#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
#define BSD_SIGNALS
#endif
#endif
#ifndef FD_SET
typedef int fd_set;
#define FD_ZERO(x) (*(x) = 0)
#define FD_SET(f, x) (*(x) = 1<<f)
#endif
#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
#define BUFFERSIZE 4096
#define JMP_RESUME 1
#define JMP_RESIZE 2
extern int optind;
extern char *optarg;
static char stdoutbuf[BUFFERSIZE];
static jmp_buf jmp_int;
char *myname = "top";
void
quit(int status)
{
screen_end();
chdir("/tmp");
exit(status);
}
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
}
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
}
RETSIGTYPE
sig_leave(int i)
{
screen_end();
exit(EX_OK);
}
RETSIGTYPE
sig_tstop(int i)
{
screen_end();
fflush(stdout);
set_signal(SIGTSTP, SIG_DFL);
release_signal(SIGTSTP);
(void) kill(0, SIGTSTP);
set_signal(SIGTSTP, sig_tstop);
screen_reinit();
longjmp(jmp_int, JMP_RESUME);
}
#ifdef SIGWINCH
RETSIGTYPE
sig_winch(int i)
{
screen_getsize();
longjmp(jmp_int, JMP_RESIZE);
}
#endif
#ifdef HAVE_SIGACTION
static sigset_t signalset;
#endif
void *
hold_signals()
{
#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
}
void
set_signals()
{
(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
}
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
}
#ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
{ "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' },
{ "display-mode", required_argument, NULL, 'm' },
{ NULL, 0, NULL, 0 },
};
#endif
void
do_arguments(globalstate *gstate, int ac, char **av)
{
int i;
optind = 1;
#ifdef HAVE_GETOPT_LONG
while ((i = getopt_long(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
#else
while ((i = getopt(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:")) != EOF)
#endif
{
switch(i)
{
#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':
i = atoi(optarg);
if (i < 0 || (i == 0 && getuid() != 0))
{
message_error(" Bad seconds delay");
}
else
{
gstate->delay = i;
}
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;
case 'M':
enable_ncpus = 1;
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 'q':
if (getuid() == 0)
{
(void) nice(-20);
}
else
{
message_error(" Option -q can only be used by root");
}
break;
default:
fprintf(stderr, "\
Top version %s\n\
Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
version_string(), myname);
exit(EX_USAGE);
}
}
if (optind < ac && *av[optind])
{
if ((i = atoiwi(av[optind])) == Invalid)
{
message_error(" Process count not a number");
}
else
{
gstate->topn = i;
}
}
}
void
do_display(globalstate *gstate)
{
int active_procs;
int i;
time_t curr_time;
caddr_t processes;
struct system_info system_info;
char *hdr;
time_mark(&(gstate->now));
curr_time = (time_t)(gstate->now.tv_sec);
get_system_info(&system_info);
processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
if (gstate->topn > 0)
{
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
{
active_procs = 0;
}
hdr = gstate->header_text;
if (gstate->fulldraw)
{
struct timespec uts;
clock_gettime(CLOCK_UPTIME, &uts);
display_clear();
i_loadave(system_info.last_pid, system_info.load_avg);
i_uptime(uts.tv_sec);
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
{
struct timespec uts;
clock_gettime(CLOCK_UPTIME, &uts);
u_loadave(system_info.last_pid, system_info.load_avg);
u_uptime(uts.tv_sec);
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
void
do_wait(globalstate *gstate)
{
struct timeval wait;
wait.tv_sec = gstate->delay;
wait.tv_usec = 0;
select(0, NULL, NULL, NULL, &wait);
}
void
do_command(globalstate *gstate)
{
int status;
struct timeval wait = {0, 0};
struct timeval now;
fd_set readfds;
unsigned char ch;
gstate->refresh = gstate->now;
gstate->refresh.tv_sec += gstate->delay;
time_get(&now);
do {
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;
}
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
if (select(32, &readfds, NULL, NULL, &wait) > 0)
{
if (read(STDIN_FILENO, &ch, 1) != 1)
{
message_error(" Read error on stdin");
quit(EX_DATAERR);
}
message_mark();
status = command_process(gstate, (int)ch);
switch(status)
{
case CMD_ERROR:
quit(EX_SOFTWARE);
case CMD_REFRESH:
return;
case CMD_UNKNOWN:
message_error(" Unknown command");
break;
case CMD_NA:
message_error(" Command not available");
}
}
time_get(&now);
} while (timercmp(&now, &(gstate->refresh), < ));
}
void
do_minidisplay(globalstate *gstate)
{
int real_delay;
struct system_info si;
real_delay = gstate->delay;
gstate->delay = 1;
time_mark(&(gstate->now));
do_command(gstate);
get_system_info(&si);
u_cpustates(si.cpustates);
gstate->delay = real_delay;
i_endscreen();
}
int
main(int argc, char *argv[])
{
char *env_top;
char **preset_argv;
int preset_argc = 0;
void *mask;
int need_mini = 1;
struct statics statics;
globalstate *gstate;
if (argc > 0)
{
if ((myname = strrchr(argv[0], '/')) == NULL)
{
myname = argv[0];
}
else
{
myname++;
}
}
#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
gstate = (globalstate *)calloc(1, sizeof(globalstate));
gstate->statics = &statics;
time_mark(NULL);
gstate->show_usernames = Yes;
gstate->topn = DEFAULT_TOPN;
gstate->delay = DEFAULT_DELAY;
gstate->fulldraw = Yes;
gstate->use_color = Yes;
gstate->interactive = Maybe;
gstate->pselect.idle = Yes;
gstate->pselect.system = No;
gstate->pselect.fullcmd = No;
gstate->pselect.command = NULL;
gstate->pselect.uid = -1;
gstate->pselect.mode = 0;
#ifdef HAVE_SETVBUF
setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
#else
#ifdef HAVE_SETBUFFER
setbuffer(stdout, stdoutbuf, BUFFERSIZE);
#endif
#endif
if ((env_top = getenv("TOP")) != NULL)
{
preset_argv = argparse(env_top, &preset_argc);
preset_argv[0] = myname;
do_arguments(gstate, preset_argc, preset_argv);
}
do_arguments(gstate, argc, argv);
#ifdef ENABLE_COLOR
env_top = getenv("TOPCOLOURS");
if (!env_top)
{
env_top = getenv("TOPCOLORS");
}
color_env_parse(env_top);
color_activate(gstate->use_color);
#endif
memzero((void *)&statics, sizeof(statics));
if (machine_init(&statics) == -1)
{
exit(EX_SOFTWARE);
}
gstate->order_namelist = string_list(statics.order_names);
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;
}
}
init_username();
gstate->smart_terminal = screen_readtermcap(gstate->interactive);
if (gstate->interactive == Maybe)
{
gstate->interactive = smart_terminal;
}
if (gstate->displays == 0)
{
gstate->displays = gstate->smart_terminal ? Infinity: 1;
}
if (gstate->delay <= 1 || !smart_terminal)
{
need_mini = 0;
}
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;
}
gstate->pselect.usernames = gstate->show_usernames;
if ((gstate->max_topn = display_init(&statics)) == -1)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(EX_OSERR);
}
if (gstate->topn == Infinity)
{
gstate->topn = INT_MAX;
}
else if (gstate->topn > gstate->max_topn)
{
#if 0
message_error(" This terminal can only display %d processes",
gstate->max_topn);
#endif
}
#ifdef ENABLE_COLOR
if (gstate->show_tags)
{
color_dump(stdout);
exit(EX_OK);
}
#endif
mask = hold_signals();
screen_init();
set_signals();
if (setjmp(jmp_int) != 0)
{
if ((gstate->max_topn = display_resize()) == -1)
{
quit(EX_OSERR);
}
gstate->fulldraw = Yes;
release_signals(mask);
}
else
{
release_signals(mask);
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);
gstate->show_cpustates = Yes;
need_mini = 0;
}
}
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_display(gstate);
quit(EX_OK);
return 1;
}