998 lines
22 KiB
C
998 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;
|
|
|
|
sigemptyset(&action.sa_mask);
|
|
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. */
|
|
}
|