Changeset View
Changeset View
Standalone View
Standalone View
contrib/top/top.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
char *copyright = | |||||
"Copyright (c) 1984 through 1996, William LeFebvre"; | |||||
/* | /* | ||||
* Top users/processes display for Unix | * Copyright (c) 1984 through 2008, William LeFebvre | ||||
* Version 3 | * All rights reserved. | ||||
* | * | ||||
* This program may be freely redistributed, | * Redistribution and use in source and binary forms, with or without | ||||
* but this entire comment MUST remain intact. | * modification, are permitted provided that the following conditions are met: | ||||
* | * | ||||
* Copyright (c) 1984, 1989, William LeFebvre, Rice University | * * Redistributions of source code must retain the above copyright | ||||
* Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University | * notice, this list of conditions and the following disclaimer. | ||||
* Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory | |||||
* Copyright (c) 1996, William LeFebvre, Group sys Consulting | |||||
* | * | ||||
* * 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. | |||||
* | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
/* | char *copyright = | ||||
* See the file "Changes" for information on version-to-version changes. | "Copyright (c) 1984 through 2008, William LeFebvre"; | ||||
*/ | |||||
/* | /* | ||||
* This file contains "main" and other high-level routines. | * 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). | |||||
*/ | */ | ||||
/* | |||||
* The following preprocessor variables, when defined, are used to | |||||
* distinguish between different Unix implementations: | |||||
* | |||||
* SIGHOLD - use SVR4 sighold function when defined | |||||
* SIGRELSE - use SVR4 sigrelse function when defined | |||||
* FD_SET - macros FD_SET and FD_ZERO are used when defined | |||||
*/ | |||||
#include "os.h" | #include "os.h" | ||||
#include <signal.h> | |||||
#include <sys/jail.h> | #include <setjmp.h> | ||||
#include <sys/time.h> | |||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <errno.h> | #include <sys/types.h> | ||||
#include <sys/uio.h> | |||||
#include <jail.h> | #include <jail.h> | ||||
#include <setjmp.h> | |||||
#include <signal.h> | |||||
#include <unistd.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 */ | /* includes specific to top */ | ||||
#include "commands.h" | |||||
#include "display.h" /* interface to display package */ | |||||
#include "screen.h" /* interface to screen package */ | |||||
#include "top.h" | #include "top.h" | ||||
#include "top.local.h" | |||||
#include "boolean.h" | |||||
#include "machine.h" | #include "machine.h" | ||||
#include "utils.h" | #include "globalstate.h" | ||||
#include "commands.h" | |||||
#include "display.h" | |||||
#include "screen.h" | |||||
#include "boolean.h" | |||||
#include "username.h" | #include "username.h" | ||||
#include "utils.h" | |||||
#include "version.h" | |||||
#ifdef ENABLE_COLOR | |||||
#include "color.h" | |||||
#endif | |||||
/* Size of the stdio buffer given to stdout */ | /* definitions */ | ||||
#define Buffersize 2048 | #define BUFFERSIZE 4096 | ||||
#define JMP_RESUME 1 | |||||
#define JMP_RESIZE 2 | |||||
/* The buffer that stdio will use */ | /* externs for getopt: */ | ||||
char stdoutbuf[Buffersize]; | |||||
/* build Signal masks */ | |||||
#define Smask(s) (1 << ((s) - 1)) | |||||
/* for getopt: */ | |||||
extern int optind; | extern int optind; | ||||
extern char *optarg; | extern char *optarg; | ||||
/* imported from screen.c */ | /* statics */ | ||||
extern int overstrike; | static char stdoutbuf[BUFFERSIZE]; | ||||
static jmp_buf jmp_int; | |||||
static int fmt_flags = 0; | /* globals */ | ||||
int pcpu_stats = No; | char *myname = "top"; | ||||
/* signal handling routines */ | void | ||||
sigret_t leave(); | quit(int status) | ||||
sigret_t tstop(); | |||||
#ifdef SIGWINCH | |||||
sigret_t winch(); | |||||
#endif | |||||
volatile sig_atomic_t leaveflag; | { | ||||
volatile sig_atomic_t tstopflag; | screen_end(); | ||||
volatile sig_atomic_t winchflag; | chdir("/tmp"); | ||||
exit(status); | |||||
/* NOTREACHED */ | |||||
} | |||||
/* internal routines */ | /* | ||||
void quit(); | * signal handlers | ||||
*/ | |||||
/* values which need to be accessed by signal handlers */ | void | ||||
static int max_topn; /* maximum displayable processes */ | set_signal(int sig, RETSIGTYPE (*handler)(int)) | ||||
/* miscellaneous things */ | { | ||||
struct process_select ps; | #ifdef HAVE_SIGACTION | ||||
char *myname = "top"; | struct sigaction action; | ||||
jmp_buf jmp_int; | |||||
/* routines that don't return int */ | action.sa_handler = handler; | ||||
action.sa_flags = 0; | |||||
(void) sigaction(sig, &action, NULL); | |||||
#else | |||||
(void) signal(sig, handler); | |||||
#endif | |||||
} | |||||
char *username(); | void | ||||
char *ctime(); | release_signal(int sig) | ||||
char *kill_procs(); | |||||
char *renice_procs(); | |||||
#ifdef ORDER | { | ||||
extern int (*compares[])(); | #ifdef HAVE_SIGACTION | ||||
#else | sigset_t set; | ||||
extern int proc_compare(); | sigemptyset(&set); | ||||
extern int io_compare(); | sigaddset(&set, sig); | ||||
sigprocmask(SIG_UNBLOCK, &set, NULL); | |||||
#endif | #endif | ||||
time_t time(); | |||||
caddr_t get_process_info(); | #ifdef HAVE_SIGHOLD | ||||
sigrelse(sig); | |||||
#endif | |||||
/* different routines for displaying the user's identification */ | #ifdef BSD_SIGNALS | ||||
/* (values assigned to get_userid) */ | (void) sigsetmask(sigblock(0) & ~(sigmask(sig))); | ||||
char *username(); | #endif | ||||
char *itoa7(); | } | ||||
/* pointers to display routines */ | RETSIGTYPE | ||||
void (*d_loadave)() = i_loadave; | sig_leave(int i) /* exit under normal conditions -- INT handler */ | ||||
void (*d_procstates)() = i_procstates; | |||||
void (*d_cpustates)() = i_cpustates; | |||||
void (*d_memory)() = i_memory; | |||||
void (*d_arc)() = i_arc; | |||||
void (*d_swap)() = i_swap; | |||||
void (*d_message)() = i_message; | |||||
void (*d_header)() = i_header; | |||||
void (*d_process)() = i_process; | |||||
void reset_display(void); | { | ||||
screen_end(); | |||||
exit(EX_OK); | |||||
} | |||||
RETSIGTYPE | |||||
sig_tstop(int i) /* SIGTSTP handler */ | |||||
int | { | ||||
main(argc, argv) | /* move to the lower left */ | ||||
screen_end(); | |||||
fflush(stdout); | |||||
int argc; | /* default the signal handler action */ | ||||
char *argv[]; | 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 | |||||
RETSIGTYPE | |||||
sig_winch(int i) /* SIGWINCH handler */ | |||||
{ | { | ||||
register int i; | /* reascertain the screen dimensions */ | ||||
register int active_procs; | screen_getsize(); | ||||
register int change; | |||||
struct system_info system_info; | /* jump back to a known place in the main loop */ | ||||
struct statics statics; | longjmp(jmp_int, JMP_RESIZE); | ||||
caddr_t processes; | } | ||||
#endif | |||||
static char tempbuf1[50]; | #ifdef HAVE_SIGACTION | ||||
static char tempbuf2[50]; | static sigset_t signalset; | ||||
int old_sigmask; /* only used for BSD-style signals */ | |||||
int topn = Default_TOPN; | |||||
int delay = Default_DELAY; | |||||
int displays = 0; /* indicates unspecified */ | |||||
int sel_ret = 0; | |||||
time_t curr_time; | |||||
char *(*get_userid)() = username; | |||||
char *uname_field = "USERNAME"; | |||||
char *header_text; | |||||
char *env_top; | |||||
char **preset_argv; | |||||
int preset_argc = 0; | |||||
char **av; | |||||
int ac; | |||||
char dostates = No; | |||||
char do_unames = Yes; | |||||
char interactive = Maybe; | |||||
char warnings = 0; | |||||
#if Default_TOPN == Infinity | |||||
char topn_specified = No; | |||||
#endif | #endif | ||||
char ch; | |||||
char *iptr; | void * | ||||
char no_command = 1; | hold_signals() | ||||
struct timeval timeout; | |||||
#ifdef ORDER | { | ||||
char *order_name = NULL; | #ifdef HAVE_SIGACTION | ||||
int order_index = 0; | sigemptyset(&signalset); | ||||
sigaddset(&signalset, SIGINT); | |||||
sigaddset(&signalset, SIGQUIT); | |||||
sigaddset(&signalset, SIGTSTP); | |||||
#ifdef SIGWINCH | |||||
sigaddset(&signalset, SIGWINCH); | |||||
#endif | #endif | ||||
#ifndef FD_SET | sigprocmask(SIG_BLOCK, &signalset, NULL); | ||||
/* FD_SET and friends are not present: fake it */ | return (void *)(&signalset); | ||||
typedef int fd_set; | |||||
#define FD_ZERO(x) (*(x) = 0) | |||||
#define FD_SET(f, x) (*(x) = 1<<f) | |||||
#endif | #endif | ||||
fd_set readfds; | |||||
#ifdef ORDER | #ifdef HAVE_SIGHOLD | ||||
static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJo"; | sighold(SIGINT); | ||||
#else | sighold(SIGQUIT); | ||||
static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJ"; | sighold(SIGTSTP); | ||||
#ifdef SIGWINCH | |||||
sighold(SIGWINCH); | |||||
return NULL; | |||||
#endif | #endif | ||||
/* these defines enumerate the "strchr"s of the commands in command_chars */ | |||||
#define CMD_redraw 0 | |||||
#define CMD_update 1 | |||||
#define CMD_quit 2 | |||||
#define CMD_help1 3 | |||||
#define CMD_help2 4 | |||||
#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */ | |||||
#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */ | |||||
#define CMD_number1 6 | |||||
#define CMD_number2 7 | |||||
#define CMD_delay 8 | |||||
#define CMD_displays 9 | |||||
#define CMD_kill 10 | |||||
#define CMD_renice 11 | |||||
#define CMD_idletog 12 | |||||
#define CMD_idletog2 13 | |||||
#define CMD_user 14 | |||||
#define CMD_selftog 15 | |||||
#define CMD_thrtog 16 | |||||
#define CMD_viewtog 17 | |||||
#define CMD_viewsys 18 | |||||
#define CMD_wcputog 19 | |||||
#define CMD_showargs 20 | |||||
#define CMD_jidtog 21 | |||||
#define CMD_kidletog 22 | |||||
#define CMD_pcputog 23 | |||||
#define CMD_jail 24 | |||||
#ifdef ORDER | |||||
#define CMD_order 25 | |||||
#endif | #endif | ||||
/* set the buffer for stdout */ | #ifdef BSD_SIGNALS | ||||
#ifdef DEBUG | int mask; | ||||
extern FILE *debug; | #ifdef SIGWINCH | ||||
debug = fopen("debug.run", "w"); | mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | | ||||
setbuffer(stdout, NULL, 0); | sigmask(SIGTSTP) | sigmask(SIGWINCH)); | ||||
#else | #else | ||||
setbuffer(stdout, stdoutbuf, Buffersize); | mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP)); | ||||
return (void *)mask; | |||||
#endif | #endif | ||||
#endif | |||||
/* get our name */ | |||||
if (argc > 0) | |||||
{ | |||||
if ((myname = strrchr(argv[0], '/')) == 0) | |||||
{ | |||||
myname = argv[0]; | |||||
} | } | ||||
else | |||||
void | |||||
set_signals() | |||||
{ | { | ||||
myname++; | (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 | |||||
} | } | ||||
} | |||||
/* initialize some selection options */ | void | ||||
ps.idle = Yes; | release_signals(void *parm) | ||||
ps.self = -1; | |||||
ps.system = No; | |||||
ps.uid = -1; | |||||
ps.thread = No; | |||||
ps.wcpu = 1; | |||||
ps.jid = -1; | |||||
ps.jail = No; | |||||
ps.kidle = Yes; | |||||
ps.command = NULL; | |||||
/* get preset options from the environment */ | |||||
if ((env_top = getenv("TOP")) != NULL) | |||||
{ | { | ||||
av = preset_argv = argparse(env_top, &preset_argc); | #ifdef HAVE_SIGACTION | ||||
ac = preset_argc; | sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL); | ||||
#endif | |||||
/* set the dummy argument to an explanatory message, in case | #ifdef HAVE_SIGHOLD | ||||
getopt encounters a bad argument */ | sigrelse(SIGINT); | ||||
preset_argv[0] = "while processing environment"; | sigrelse(SIGQUIT); | ||||
sigrelse(SIGTSTP); | |||||
#ifdef SIGWINCH | |||||
sigrelse(SIGWINCH); | |||||
#endif | |||||
#endif | |||||
#ifdef BSD_SIGNALS | |||||
(void) sigsetmask((int)parm); | |||||
#endif | |||||
} | } | ||||
/* process options */ | /* | ||||
do { | * void do_arguments(globalstate *gstate, int ac, char **av) | ||||
/* if we're done doing the presets, then process the real arguments */ | * | ||||
if (preset_argc == 0) | * 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[] = { | |||||
{ "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' }, | |||||
{ "jails", no_argument, NULL, 'j' }, | |||||
{ "jail", required_argument, NULL, 'J' }, | |||||
{ "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) | |||||
{ | { | ||||
ac = argc; | int i; | ||||
av = argv; | |||||
/* this should keep getopt happy... */ | /* this appears to keep getopt happy */ | ||||
optind = 1; | optind = 1; | ||||
} | |||||
while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:t")) != EOF) | #ifdef HAVE_GETOPT_LONG | ||||
while ((i = getopt_long(ac, av, "CDHSITabcijnqtuvJ:s:d:U:o:m:", longopts, NULL)) != -1) | |||||
#else | |||||
while ((i = getopt(ac, av, "CDHSITabcijnqtuvJ:s:d:U:o:m:")) != EOF) | |||||
#endif | |||||
{ | { | ||||
switch(i) | switch(i) | ||||
{ | { | ||||
case 'v': /* show version number */ | #ifdef ENABLE_COLOR | ||||
fprintf(stderr, "%s: version %s\n", | case 'C': | ||||
myname, version_string()); | gstate->use_color = !gstate->use_color; | ||||
exit(1); | |||||
break; | break; | ||||
#endif | |||||
case 'u': /* toggle uid/username display */ | case 'D': | ||||
do_unames = !do_unames; | debug_set(1); | ||||
break; | break; | ||||
case 'U': /* display only username's processes */ | case 'v': | ||||
if ((ps.uid = userid(optarg)) == -1) | fprintf(stderr, "%s: version %s\n", myname, version_string()); | ||||
{ | exit(EX_OK); | ||||
fprintf(stderr, "%s: unknown user\n", optarg); | |||||
exit(1); | |||||
} | |||||
break; | break; | ||||
case 'S': /* show system processes */ | case 'b': | ||||
ps.system = !ps.system; | case 'n': | ||||
gstate->interactive = No; | |||||
break; | break; | ||||
case 'I': /* show idle processes */ | case 'a': | ||||
ps.idle = !ps.idle; | gstate->displays = Infinity; | ||||
gstate->topn = Infinity; | |||||
break; | break; | ||||
case 'i': /* go interactive regardless */ | case 'i': | ||||
interactive = Yes; | gstate->interactive = Yes; | ||||
break; | break; | ||||
case 'n': /* batch, or non-interactive */ | case 'o': | ||||
case 'b': | gstate->order_name = optarg; | ||||
interactive = No; | |||||
break; | break; | ||||
case 'a': | case 'd': | ||||
fmt_flags ^= FMT_SHOWARGS; | i = atoiwi(optarg); | ||||
break; | if (i == Invalid || i == 0) | ||||
case 'd': /* number of displays to show */ | |||||
if ((i = atoiwi(optarg)) == Invalid || i == 0) | |||||
{ | { | ||||
fprintf(stderr, | message_error(" Bad display count"); | ||||
"%s: warning: display count should be positive -- option ignored\n", | |||||
myname); | |||||
warnings++; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
displays = i; | gstate->displays = i; | ||||
} | } | ||||
break; | break; | ||||
case 's': | case 's': | ||||
if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) | i = atoi(optarg); | ||||
if (i < 0 || (i == 0 && getuid() != 0)) | |||||
{ | { | ||||
fprintf(stderr, | message_error(" Bad seconds delay"); | ||||
"%s: warning: seconds delay should be positive -- using default\n", | |||||
myname); | |||||
delay = Default_DELAY; | |||||
warnings++; | |||||
} | } | ||||
else | |||||
{ | |||||
gstate->delay = i; | |||||
} | |||||
break; | break; | ||||
case 'q': /* be quick about it */ | case 'u': | ||||
/* only allow this if user is really root */ | gstate->show_usernames = !gstate->show_usernames; | ||||
if (getuid() == 0) | break; | ||||
case 'U': | |||||
i = userid(optarg); | |||||
if (i == -1) | |||||
{ | { | ||||
/* be very un-nice! */ | message_error(" Unknown user '%s'", optarg); | ||||
(void) nice(-20); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
fprintf(stderr, | gstate->pselect.uid = i; | ||||
"%s: warning: `-q' option can only be used by root\n", | |||||
myname); | |||||
warnings++; | |||||
} | } | ||||
break; | break; | ||||
case 'm': /* select display mode */ | case 'm': | ||||
if (strcmp(optarg, "io") == 0) { | /* Argument may be 'io' or 'cpu', '1', or '0'. */ | ||||
displaymode = DISP_IO; | if (strcmp(optarg, "io") == 0) | ||||
} else if (strcmp(optarg, "cpu") == 0) { | i = 1; | ||||
displaymode = DISP_CPU; | else if (strcmp(optarg, "cpu") == 0) | ||||
} else { | i = 0; | ||||
fprintf(stderr, | else | ||||
"%s: warning: `-m' option can only take args " | i = atoi(optarg); | ||||
"'io' or 'cpu'\n", | gstate->pselect.mode = i; | ||||
myname); | |||||
exit(1); | |||||
} | |||||
break; | break; | ||||
case 'o': /* select sort order */ | case 'S': | ||||
#ifdef ORDER | gstate->pselect.system = !gstate->pselect.system; | ||||
order_name = optarg; | |||||
#else | |||||
fprintf(stderr, | |||||
"%s: this platform does not support arbitrary ordering. Sorry.\n", | |||||
myname); | |||||
warnings++; | |||||
#endif | |||||
break; | break; | ||||
case 't': | case 'I': | ||||
ps.self = (ps.self == -1) ? getpid() : -1; | gstate->pselect.idle = !gstate->pselect.idle; | ||||
break; | break; | ||||
case 'C': | #ifdef ENABLE_COLOR | ||||
ps.wcpu = !ps.wcpu; | case 'T': | ||||
gstate->show_tags = 1; | |||||
break; | break; | ||||
#endif | |||||
case 'H': | case 'c': | ||||
ps.thread = !ps.thread; | gstate->pselect.fullcmd = !gstate->pselect.fullcmd; | ||||
break; | break; | ||||
case 'j': | case 't': | ||||
ps.jail = !ps.jail; | case 'H': | ||||
gstate->pselect.threads = !gstate->pselect.threads; | |||||
break; | break; | ||||
case 'J': /* display only jail's processes */ | case 'q': /* be quick about it */ | ||||
if ((ps.jid = jail_getid(optarg)) == -1) | /* only allow this if user is really root */ | ||||
if (getuid() == 0) | |||||
{ | { | ||||
fprintf(stderr, "%s: unknown jail\n", optarg); | /* be very un-nice! */ | ||||
exit(1); | (void) nice(-20); | ||||
} | } | ||||
ps.jail = 1; | else | ||||
{ | |||||
message_error(" Option -q can only be used by root"); | |||||
} | |||||
break; | break; | ||||
case 'P': | |||||
pcpu_stats = !pcpu_stats; | |||||
break; | |||||
case 'z': | case 'J': | ||||
ps.kidle = !ps.kidle; | gstate->statics->flags.jids = 1; | ||||
if ((gstate->pselect.jail = jail_getid(optarg)) == -1) | |||||
{ | |||||
message_error(" No such jail"); | |||||
} | |||||
else | |||||
{ | |||||
gstate->pselect.filter_jail = 1; | |||||
} | |||||
/* implies -j, so fall through. */ | |||||
case 'j': | |||||
gstate->statics->flags.jids = 1; | |||||
break; | break; | ||||
default: | default: | ||||
fprintf(stderr, | fprintf(stderr, "\ | ||||
"Top version %s\n" | Top version %s\n\ | ||||
"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n" | Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", | ||||
" [-J jail] [-U username] [number]\n", | |||||
version_string(), myname); | version_string(), myname); | ||||
exit(1); | exit(EX_USAGE); | ||||
} | } | ||||
} | } | ||||
/* get count of top processes to display (if any) */ | /* get count of top processes to display */ | ||||
if (optind < ac) | if (optind < ac && *av[optind]) | ||||
{ | { | ||||
if ((topn = atoiwi(av[optind])) == Invalid) | if ((i = atoiwi(av[optind])) == Invalid) | ||||
{ | { | ||||
fprintf(stderr, | message_error(" Process count not a number"); | ||||
"%s: warning: process display count should be non-negative -- using default\n", | |||||
myname); | |||||
warnings++; | |||||
} | } | ||||
#if Default_TOPN == Infinity | |||||
else | else | ||||
{ | { | ||||
topn_specified = Yes; | gstate->topn = i; | ||||
} | } | ||||
#endif | |||||
} | } | ||||
/* tricky: remember old value of preset_argc & set preset_argc = 0 */ | |||||
i = preset_argc; | |||||
preset_argc = 0; | |||||
/* repeat only if we really did the preset arguments */ | |||||
} while (i != 0); | |||||
/* set constants for username/uid display correctly */ | |||||
if (!do_unames) | |||||
{ | |||||
uname_field = " UID "; | |||||
get_userid = itoa7; | |||||
} | } | ||||
/* initialize the kernel memory interface */ | void | ||||
if (machine_init(&statics, do_unames) == -1) | do_display(globalstate *gstate) | ||||
{ | |||||
exit(1); | |||||
} | |||||
#ifdef ORDER | |||||
/* determine sorting order index, if necessary */ | |||||
if (order_name != NULL) | |||||
{ | { | ||||
if ((order_index = string_index(order_name, statics.order_names)) == -1) | int active_procs; | ||||
{ | int i; | ||||
char **pp; | time_t curr_time; | ||||
caddr_t processes; | |||||
struct system_info system_info; | |||||
char *hdr; | |||||
fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", | /* get the time */ | ||||
myname, order_name); | time_mark(&(gstate->now)); | ||||
fprintf(stderr, "\tTry one of these:"); | curr_time = (time_t)(gstate->now.tv_sec); | ||||
pp = statics.order_names; | |||||
while (*pp != NULL) | |||||
{ | |||||
fprintf(stderr, " %s", *pp++); | |||||
} | |||||
fputc('\n', stderr); | |||||
exit(1); | |||||
} | |||||
} | |||||
#endif | |||||
#ifdef no_initialization_needed | /* get the current stats */ | ||||
/* initialize the hashing stuff */ | get_system_info(&system_info); | ||||
if (do_unames) | |||||
{ | |||||
init_hash(); | |||||
} | |||||
#endif | |||||
/* initialize termcap */ | /* get the current processes */ | ||||
init_termcap(interactive); | processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); | ||||
/* get the string to use for the process area header */ | /* determine number of processes to actually display */ | ||||
header_text = format_header(uname_field); | if (gstate->topn > 0) | ||||
/* initialize display interface */ | |||||
if ((max_topn = display_init(&statics)) == -1) | |||||
{ | { | ||||
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); | /* this number will be the smallest of: active processes, | ||||
exit(4); | number user requested, number current screen accomodates */ | ||||
} | active_procs = system_info.P_ACTIVE; | ||||
if (active_procs > gstate->topn) | |||||
/* print warning if user requested more processes than we can display */ | |||||
if (topn > max_topn) | |||||
{ | { | ||||
fprintf(stderr, | active_procs = gstate->topn; | ||||
"%s: warning: this terminal can only display %d processes.\n", | |||||
myname, max_topn); | |||||
warnings++; | |||||
} | } | ||||
if (active_procs > gstate->max_topn) | |||||
/* adjust for topn == Infinity */ | |||||
if (topn == Infinity) | |||||
{ | { | ||||
/* | active_procs = gstate->max_topn; | ||||
* For smart terminals, infinity really means everything that can | |||||
* be displayed, or Largest. | |||||
* On dumb terminals, infinity means every process in the system! | |||||
* We only really want to do that if it was explicitly specified. | |||||
* This is always the case when "Default_TOPN != Infinity". But if | |||||
* topn wasn't explicitly specified and we are on a dumb terminal | |||||
* and the default is Infinity, then (and only then) we use | |||||
* "Nominal_TOPN" instead. | |||||
*/ | |||||
#if Default_TOPN == Infinity | |||||
topn = smart_terminal ? Largest : | |||||
(topn_specified ? Largest : Nominal_TOPN); | |||||
#else | |||||
topn = Largest; | |||||
#endif | |||||
} | } | ||||
/* set header display accordingly */ | |||||
display_header(topn > 0); | |||||
/* determine interactive state */ | |||||
if (interactive == Maybe) | |||||
{ | |||||
interactive = smart_terminal; | |||||
} | } | ||||
else | |||||
/* if # of displays not specified, fill it in */ | |||||
if (displays == 0) | |||||
{ | { | ||||
displays = smart_terminal ? Infinity : 1; | /* dont show any */ | ||||
active_procs = 0; | |||||
} | } | ||||
/* hold interrupt signals while setting up the screen and the handlers */ | #ifdef HAVE_FORMAT_PROCESS_HEADER | ||||
#ifdef SIGHOLD | /* get the process header to use */ | ||||
sighold(SIGINT); | hdr = format_process_header(&(gstate->pselect), processes, active_procs); | ||||
sighold(SIGQUIT); | |||||
sighold(SIGTSTP); | |||||
#else | #else | ||||
old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); | hdr = gstate->header_text; | ||||
#endif | #endif | ||||
init_screen(); | |||||
(void) signal(SIGINT, leave); | |||||
(void) signal(SIGQUIT, leave); | |||||
(void) signal(SIGTSTP, tstop); | |||||
#ifdef SIGWINCH | |||||
(void) signal(SIGWINCH, winch); | |||||
#endif | |||||
#ifdef SIGRELSE | |||||
sigrelse(SIGINT); | |||||
sigrelse(SIGQUIT); | |||||
sigrelse(SIGTSTP); | |||||
#else | |||||
(void) sigsetmask(old_sigmask); | |||||
#endif | |||||
if (warnings) | |||||
{ | |||||
fputs("....", stderr); | |||||
fflush(stderr); /* why must I do this? */ | |||||
sleep((unsigned)(3 * warnings)); | |||||
fputc('\n', stderr); | |||||
} | |||||
restart: | /* full screen or update? */ | ||||
if (gstate->fulldraw) | |||||
/* | |||||
* main loop -- repeat while display count is positive or while it | |||||
* indicates infinity (by being -1) | |||||
*/ | |||||
while ((displays == -1) || (displays-- > 0)) | |||||
{ | { | ||||
int (*compare)(); | display_clear(); | ||||
i_loadave(system_info.last_pid, system_info.load_avg); | |||||
i_uptime(&(gstate->statics->boottime), &curr_time); | |||||
/* get the current stats */ | |||||
get_system_info(&system_info); | |||||
#ifdef ORDER | |||||
compare = compares[order_index]; | |||||
#else | |||||
if (displaymode == DISP_CPU) | |||||
compare = proc_compare; | |||||
else | |||||
compare = io_compare; | |||||
#endif | |||||
/* get the current set of processes */ | |||||
processes = | |||||
get_process_info(&system_info, &ps, compare); | |||||
/* display the load averages */ | |||||
(*d_loadave)(system_info.last_pid, | |||||
system_info.load_avg); | |||||
/* display the current time */ | |||||
/* this method of getting the time SHOULD be fairly portable */ | |||||
time(&curr_time); | |||||
i_uptime(&system_info.boottime, &curr_time); | |||||
i_timeofday(&curr_time); | i_timeofday(&curr_time); | ||||
i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); | |||||
/* display process state breakdown */ | if (gstate->show_cpustates) | ||||
(*d_procstates)(system_info.p_total, | |||||
system_info.procstates); | |||||
/* display the cpu state percentage breakdown */ | |||||
if (dostates) /* but not the first time */ | |||||
{ | { | ||||
(*d_cpustates)(system_info.cpustates); | i_cpustates(system_info.cpustates); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
/* we'll do it next time */ | |||||
if (smart_terminal) | if (smart_terminal) | ||||
{ | { | ||||
z_cpustates(); | z_cpustates(); | ||||
} | } | ||||
else | gstate->show_cpustates = Yes; | ||||
{ | |||||
putchar('\n'); | |||||
} | } | ||||
dostates = Yes; | i_kernel(system_info.kernel); | ||||
} | i_memory(system_info.memory); | ||||
i_arc(system_info.arc); | |||||
/* display memory stats */ | i_swap(system_info.swap); | ||||
(*d_memory)(system_info.memory); | i_message(&(gstate->now)); | ||||
(*d_arc)(system_info.arc); | i_header(hdr); | ||||
for (i = 0; i < active_procs; i++) | |||||
/* display swap stats */ | |||||
(*d_swap)(system_info.swap); | |||||
/* handle message area */ | |||||
(*d_message)(); | |||||
/* update the header area */ | |||||
(*d_header)(header_text); | |||||
if (topn > 0) | |||||
{ | { | ||||
/* determine number of processes to actually display */ | i_process(i, format_next_process(processes, gstate->get_userid)); | ||||
/* 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 > topn) | |||||
{ | |||||
active_procs = topn; | |||||
} | } | ||||
if (active_procs > max_topn) | i_endscreen(); | ||||
if (gstate->smart_terminal) | |||||
{ | { | ||||
active_procs = max_topn; | gstate->fulldraw = No; | ||||
} | } | ||||
} | |||||
/* now show the top "n" processes. */ | 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_arc(system_info.arc); | |||||
u_swap(system_info.swap); | |||||
u_message(&(gstate->now)); | |||||
u_header(hdr); | |||||
for (i = 0; i < active_procs; i++) | for (i = 0; i < active_procs; i++) | ||||
{ | { | ||||
(*d_process)(i, format_next_process(processes, get_userid, | u_process(i, format_next_process(processes, gstate->get_userid)); | ||||
fmt_flags)); | |||||
} | } | ||||
u_endscreen(); | |||||
} | } | ||||
else | |||||
{ | |||||
i = 0; | |||||
} | } | ||||
/* do end-screen processing */ | #ifdef DEBUG | ||||
u_endscreen(i); | void | ||||
timeval_xdprint(char *s, struct timeval tv) | |||||
/* now, flush the output buffer */ | |||||
if (fflush(stdout) != 0) | |||||
{ | { | ||||
new_message(MT_standout, " Write error on stdout"); | xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); | ||||
putchar('\r'); | |||||
quit(1); | |||||
/*NOTREACHED*/ | |||||
} | } | ||||
#endif | |||||
/* only do the rest if we have more displays to show */ | void | ||||
if (displays) | do_wait(globalstate *gstate) | ||||
{ | { | ||||
/* switch out for new display on smart terminals */ | struct timeval wait; | ||||
if (smart_terminal) | |||||
{ | wait.tv_sec = gstate->delay; | ||||
if (overstrike) | wait.tv_usec = 0; | ||||
{ | select(0, NULL, NULL, NULL, &wait); | ||||
reset_display(); | |||||
} | } | ||||
else | |||||
void | |||||
do_command(globalstate *gstate) | |||||
{ | { | ||||
d_loadave = u_loadave; | int status; | ||||
d_procstates = u_procstates; | struct timeval wait = {0, 0}; | ||||
d_cpustates = u_cpustates; | struct timeval now; | ||||
d_memory = u_memory; | fd_set readfds; | ||||
d_arc = u_arc; | unsigned char ch; | ||||
d_swap = u_swap; | |||||
d_message = u_message; | |||||
d_header = u_header; | |||||
d_process = u_process; | |||||
} | |||||
} | |||||
no_command = Yes; | /* calculate new refresh time */ | ||||
if (!interactive) | gstate->refresh = gstate->now; | ||||
gstate->refresh.tv_sec += gstate->delay; | |||||
time_get(&now); | |||||
/* loop waiting for time to expire */ | |||||
do { | |||||
/* calculate time to wait */ | |||||
if (gstate->delay > 0) | |||||
{ | { | ||||
sleep(delay); | wait = gstate->refresh; | ||||
if (leaveflag) { | wait.tv_usec -= now.tv_usec; | ||||
end_screen(); | if (wait.tv_usec < 0) | ||||
exit(0); | { | ||||
wait.tv_usec += 1000000; | |||||
wait.tv_sec--; | |||||
} | } | ||||
wait.tv_sec -= now.tv_sec; | |||||
} | } | ||||
else while (no_command) | |||||
{ | |||||
/* assume valid command unless told otherwise */ | |||||
no_command = No; | |||||
/* set up arguments for select with timeout */ | /* set up arguments for select on stdin (0) */ | ||||
FD_ZERO(&readfds); | FD_ZERO(&readfds); | ||||
FD_SET(0, &readfds); /* for standard input */ | FD_SET(STDIN_FILENO, &readfds); | ||||
timeout.tv_sec = delay; | |||||
timeout.tv_usec = 0; | |||||
if (leaveflag) { | /* wait for something to read or time out */ | ||||
end_screen(); | if (select(32, &readfds, NULL, NULL, &wait) > 0) | ||||
exit(0); | { | ||||
/* read it */ | |||||
if (read(STDIN_FILENO, &ch, 1) != 1) | |||||
{ | |||||
/* read error */ | |||||
message_error(" Read error on stdin"); | |||||
quit(EX_DATAERR); | |||||
/*NOTREACHED*/ | |||||
} | } | ||||
if (tstopflag) { | /* mark pending messages as old */ | ||||
/* move to the lower left */ | message_mark(); | ||||
end_screen(); | |||||
fflush(stdout); | |||||
/* default the signal handler action */ | /* dispatch */ | ||||
(void) signal(SIGTSTP, SIG_DFL); | status = command_process(gstate, (int)ch); | ||||
switch(status) | |||||
{ | |||||
case CMD_ERROR: | |||||
quit(EX_SOFTWARE); | |||||
/*NOTREACHED*/ | |||||
/* unblock the signal and send ourselves one */ | case CMD_REFRESH: | ||||
#ifdef SIGRELSE | return; | ||||
sigrelse(SIGTSTP); | |||||
#else | |||||
(void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); | |||||
#endif | |||||
(void) kill(0, SIGTSTP); | |||||
/* reset the signal handler */ | case CMD_UNKNOWN: | ||||
(void) signal(SIGTSTP, tstop); | message_error(" Unknown command"); | ||||
break; | |||||
/* reinit screen */ | case CMD_NA: | ||||
reinit_screen(); | message_error(" Command not available"); | ||||
reset_display(); | |||||
tstopflag = 0; | |||||
goto restart; | |||||
} | } | ||||
} | |||||
if (winchflag) { | /* get new time */ | ||||
/* reascertain the screen dimensions */ | time_get(&now); | ||||
get_screensize(); | } while (timercmp(&now, &(gstate->refresh), < )); | ||||
} | |||||
/* tell display to resize */ | void | ||||
max_topn = display_resize(); | do_minidisplay(globalstate *gstate) | ||||
/* reset the signal handler */ | { | ||||
(void) signal(SIGWINCH, winch); | int real_delay; | ||||
struct system_info si; | |||||
reset_display(); | /* save the real delay and substitute 1 second */ | ||||
winchflag = 0; | real_delay = gstate->delay; | ||||
goto restart; | 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(); | |||||
} | } | ||||
/* wait for either input or the end of the delay period */ | int | ||||
sel_ret = select(2, &readfds, NULL, NULL, &timeout); | main(int argc, char *argv[]) | ||||
if (sel_ret < 0 && errno != EINTR) | |||||
quit(0); | |||||
if (sel_ret > 0) | |||||
{ | { | ||||
int newval; | char *env_top; | ||||
char *errmsg; | char **preset_argv; | ||||
int preset_argc = 0; | |||||
void *mask; | |||||
int need_mini = 1; | |||||
/* something to read -- clear the message area first */ | struct statics statics; | ||||
clear_message(); | globalstate *gstate; | ||||
/* now read it and convert to command strchr */ | /* get our name */ | ||||
/* (use "change" as a temporary to hold strchr) */ | if (argc > 0) | ||||
if (read(0, &ch, 1) != 1) | |||||
{ | { | ||||
/* read error: either 0 or -1 */ | if ((myname = strrchr(argv[0], '/')) == 0) | ||||
new_message(MT_standout, " Read error on stdin"); | |||||
putchar('\r'); | |||||
quit(1); | |||||
/*NOTREACHED*/ | |||||
} | |||||
if ((iptr = strchr(command_chars, ch)) == NULL) | |||||
{ | { | ||||
if (ch != '\r' && ch != '\n') | myname = argv[0]; | ||||
{ | |||||
/* illegal command */ | |||||
new_message(MT_standout, " Command not understood"); | |||||
} | } | ||||
putchar('\r'); | |||||
no_command = Yes; | |||||
} | |||||
else | else | ||||
{ | { | ||||
change = iptr - command_chars; | myname++; | ||||
if (overstrike && change > CMD_OSLIMIT) | |||||
{ | |||||
/* error */ | |||||
new_message(MT_standout, | |||||
" Command cannot be handled by this terminal"); | |||||
putchar('\r'); | |||||
no_command = Yes; | |||||
} | } | ||||
else switch(change) | } | ||||
/* binary compatibility check */ | |||||
#ifdef HAVE_UNAME | |||||
{ | { | ||||
case CMD_redraw: /* redraw screen */ | struct utsname uts; | ||||
reset_display(); | |||||
break; | |||||
case CMD_update: /* merely update display */ | if (uname(&uts) == 0) | ||||
/* is the load average high? */ | |||||
if (system_info.load_avg[0] > LoadMax) | |||||
{ | { | ||||
/* yes, go home for visual feedback */ | if (strcmp(uts.machine, UNAME_HARDWARE) != 0) | ||||
go_home(); | { | ||||
fflush(stdout); | fprintf(stderr, "%s: incompatible hardware platform\n", | ||||
myname); | |||||
exit(EX_UNAVAILABLE); | |||||
} | } | ||||
break; | } | ||||
} | |||||
#endif | |||||
case CMD_quit: /* quit */ | /* initialization */ | ||||
quit(0); | gstate = (globalstate *)calloc(1, sizeof(globalstate)); | ||||
/*NOTREACHED*/ | gstate->statics = &statics; | ||||
break; | time_mark(NULL); | ||||
case CMD_help1: /* help */ | /* preset defaults for various options */ | ||||
case CMD_help2: | gstate->show_usernames = Yes; | ||||
reset_display(); | gstate->topn = DEFAULT_TOPN; | ||||
clear(); | gstate->delay = DEFAULT_DELAY; | ||||
show_help(); | gstate->fulldraw = Yes; | ||||
standout("Hit any key to continue: "); | gstate->use_color = Yes; | ||||
fflush(stdout); | gstate->interactive = Maybe; | ||||
(void) read(0, &ch, 1); | |||||
break; | |||||
case CMD_errors: /* show errors */ | /* preset defaults for process selection */ | ||||
if (error_count() == 0) | gstate->pselect.idle = Yes; | ||||
{ | gstate->pselect.system = No; | ||||
new_message(MT_standout, | gstate->pselect.fullcmd = No; | ||||
" Currently no errors to report."); | gstate->pselect.command = NULL; | ||||
putchar('\r'); | gstate->pselect.uid = -1; | ||||
no_command = Yes; | gstate->pselect.mode = 0; | ||||
} | |||||
else | |||||
{ | |||||
reset_display(); | |||||
clear(); | |||||
show_errors(); | |||||
standout("Hit any key to continue: "); | |||||
fflush(stdout); | |||||
(void) read(0, &ch, 1); | |||||
} | |||||
break; | |||||
case CMD_number1: /* new number */ | /* use a large buffer for stdout */ | ||||
case CMD_number2: | #ifdef HAVE_SETVBUF | ||||
new_message(MT_standout, | setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); | ||||
"Number of processes to show: "); | #else | ||||
newval = readline(tempbuf1, 8, Yes); | #ifdef HAVE_SETBUFFER | ||||
if (newval > -1) | setbuffer(stdout, stdoutbuf, BUFFERSIZE); | ||||
#endif | |||||
#endif | |||||
/* get preset options from the environment */ | |||||
if ((env_top = getenv("TOP")) != NULL) | |||||
{ | { | ||||
if (newval > max_topn) | preset_argv = argparse(env_top, &preset_argc); | ||||
{ | preset_argv[0] = myname; | ||||
new_message(MT_standout | MT_delayed, | do_arguments(gstate, preset_argc, preset_argv); | ||||
" This terminal can only display %d processes.", | |||||
max_topn); | |||||
putchar('\r'); | |||||
} | } | ||||
if (newval == 0) | /* 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) | |||||
{ | { | ||||
/* inhibit the header */ | env_top = getenv("TOPCOLORS"); | ||||
display_header(No); | |||||
} | } | ||||
else if (newval > topn && topn == 0) | /* must do something about error messages */ | ||||
{ | color_env_parse(env_top); | ||||
/* redraw the header */ | color_activate(gstate->use_color); | ||||
display_header(Yes); | #endif | ||||
d_header = i_header; | |||||
} | |||||
topn = newval; | |||||
} | |||||
break; | |||||
case CMD_delay: /* new seconds delay */ | /* in order to support forward compatability, we have to ensure that | ||||
new_message(MT_standout, "Seconds to delay: "); | the entire statics structure is set to a known value before we call | ||||
if ((i = readline(tempbuf1, 8, Yes)) > -1) | machine_init. This way fields that a module does not know about | ||||
will retain their default values */ | |||||
/* HACK: preserve the value of flags.jids because it was set by the -j | |||||
* option. It can't be in gstate because m_freebsd needs to see it from | |||||
* machine_init. | |||||
*/ | |||||
struct statics temp = statics; | |||||
memzero((void *)&statics, sizeof(statics)); | |||||
statics.boottime = -1; | |||||
statics.flags.jids = temp.flags.jids; | |||||
/* call the platform-specific init */ | |||||
if (machine_init(&statics) == -1) | |||||
{ | { | ||||
if ((delay = i) == 0 && getuid() != 0) | exit(EX_SOFTWARE); | ||||
{ | |||||
delay = 1; | |||||
} | } | ||||
} | |||||
clear_message(); | |||||
break; | |||||
case CMD_displays: /* change display count */ | /* create a helper list of sort order names */ | ||||
new_message(MT_standout, | gstate->order_namelist = string_list(statics.order_names); | ||||
"Displays to show (currently %s): ", | |||||
displays == -1 ? "infinite" : | /* look up chosen sorting order */ | ||||
itoa(displays)); | if (gstate->order_name != NULL) | ||||
if ((i = readline(tempbuf1, 10, Yes)) > 0) | |||||
{ | { | ||||
displays = i; | int i; | ||||
} | |||||
else if (i == 0) | |||||
{ | |||||
quit(0); | |||||
} | |||||
clear_message(); | |||||
break; | |||||
case CMD_kill: /* kill program */ | if (statics.order_names == NULL) | ||||
new_message(0, "kill "); | |||||
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) | |||||
{ | { | ||||
if ((errmsg = kill_procs(tempbuf2)) != NULL) | message_error(" This platform does not support arbitrary ordering"); | ||||
{ | |||||
new_message(MT_standout, "%s", errmsg); | |||||
putchar('\r'); | |||||
no_command = Yes; | |||||
} | } | ||||
} | else if ((i = string_index(gstate->order_name, | ||||
else | statics.order_names)) == -1) | ||||
{ | { | ||||
clear_message(); | message_error(" Sort order `%s' not recognized", gstate->order_name); | ||||
message_error(" Recognized sort orders: %s", gstate->order_namelist); | |||||
} | } | ||||
break; | |||||
case CMD_renice: /* renice program */ | |||||
new_message(0, "renice "); | |||||
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) | |||||
{ | |||||
if ((errmsg = renice_procs(tempbuf2)) != NULL) | |||||
{ | |||||
new_message(MT_standout, "%s", errmsg); | |||||
putchar('\r'); | |||||
no_command = Yes; | |||||
} | |||||
} | |||||
else | else | ||||
{ | { | ||||
clear_message(); | gstate->order_index = i; | ||||
} | } | ||||
break; | } | ||||
case CMD_idletog: | /* initialize extensions */ | ||||
case CMD_idletog2: | init_username(); | ||||
ps.idle = !ps.idle; | |||||
new_message(MT_standout | MT_delayed, | |||||
" %sisplaying idle processes.", | |||||
ps.idle ? "D" : "Not d"); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_selftog: | /* initialize termcap */ | ||||
ps.self = (ps.self == -1) ? getpid() : -1; | gstate->smart_terminal = screen_readtermcap(gstate->interactive); | ||||
new_message(MT_standout | MT_delayed, | |||||
" %sisplaying self.", | |||||
(ps.self == -1) ? "D" : "Not d"); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_user: | /* determine interactive state */ | ||||
new_message(MT_standout, | if (gstate->interactive == Maybe) | ||||
"Username to show (+ for all): "); | |||||
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) | |||||
{ | { | ||||
if (tempbuf2[0] == '+' && | gstate->interactive = smart_terminal; | ||||
tempbuf2[1] == '\0') | |||||
{ | |||||
ps.uid = -1; | |||||
} | } | ||||
else if ((i = userid(tempbuf2)) == -1) | |||||
/* if displays were not specified, choose an appropriate default */ | |||||
if (gstate->displays == 0) | |||||
{ | { | ||||
new_message(MT_standout, | gstate->displays = gstate->smart_terminal ? Infinity: 1; | ||||
" %s: unknown user", tempbuf2); | |||||
no_command = Yes; | |||||
} | } | ||||
else | |||||
/* 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) | |||||
{ | { | ||||
ps.uid = i; | need_mini = 0; | ||||
} | } | ||||
putchar('\r'); | |||||
} | |||||
else | |||||
{ | |||||
clear_message(); | |||||
} | |||||
break; | |||||
case CMD_thrtog: | #ifndef HAVE_FORMAT_PROCESS_HEADER | ||||
ps.thread = !ps.thread; | /* set constants for username/uid display */ | ||||
new_message(MT_standout | MT_delayed, | if (gstate->show_usernames) | ||||
" Displaying threads %s", | |||||
ps.thread ? "separately" : "as a count"); | |||||
header_text = format_header(uname_field); | |||||
reset_display(); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_wcputog: | |||||
ps.wcpu = !ps.wcpu; | |||||
new_message(MT_standout | MT_delayed, | |||||
" Displaying %s CPU", | |||||
ps.wcpu ? "weighted" : "raw"); | |||||
header_text = format_header(uname_field); | |||||
reset_display(); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_viewtog: | |||||
if (++displaymode == DISP_MAX) | |||||
displaymode = 0; | |||||
header_text = format_header(uname_field); | |||||
display_header(Yes); | |||||
d_header = i_header; | |||||
reset_display(); | |||||
break; | |||||
case CMD_viewsys: | |||||
ps.system = !ps.system; | |||||
break; | |||||
case CMD_showargs: | |||||
fmt_flags ^= FMT_SHOWARGS; | |||||
break; | |||||
#ifdef ORDER | |||||
case CMD_order: | |||||
new_message(MT_standout, | |||||
"Order to sort: "); | |||||
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) | |||||
{ | { | ||||
if ((i = string_index(tempbuf2, statics.order_names)) == -1) | gstate->header_text = format_header("USERNAME"); | ||||
{ | gstate->get_userid = username; | ||||
new_message(MT_standout, | |||||
" %s: unrecognized sorting order", tempbuf2); | |||||
no_command = Yes; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
order_index = i; | gstate->header_text = format_header(" UID "); | ||||
gstate->get_userid = itoa7; | |||||
} | } | ||||
putchar('\r'); | |||||
} | |||||
else | |||||
{ | |||||
clear_message(); | |||||
} | |||||
break; | |||||
#endif | #endif | ||||
case CMD_jidtog: | gstate->pselect.usernames = gstate->show_usernames; | ||||
ps.jail = !ps.jail; | |||||
new_message(MT_standout | MT_delayed, | |||||
" %sisplaying jail ID.", | |||||
ps.jail ? "D" : "Not d"); | |||||
header_text = format_header(uname_field); | |||||
reset_display(); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_jail: | /* initialize display */ | ||||
new_message(MT_standout, | if ((gstate->max_topn = display_init(&statics)) == -1) | ||||
"Jail to show (+ for all): "); | |||||
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) | |||||
{ | { | ||||
if (tempbuf2[0] == '+' && | fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); | ||||
tempbuf2[1] == '\0') | exit(EX_OSERR); | ||||
{ | |||||
ps.jid = -1; | |||||
} | } | ||||
else if ((i = jail_getid(tempbuf2)) == -1) | |||||
/* check for infinity and for overflowed screen */ | |||||
if (gstate->topn == Infinity) | |||||
{ | { | ||||
new_message(MT_standout, | gstate->topn = INT_MAX; | ||||
" %s: unknown jail", tempbuf2); | |||||
no_command = Yes; | |||||
} | } | ||||
else | else if (gstate->topn > gstate->max_topn) | ||||
{ | { | ||||
ps.jid = i; | message_error(" This terminal can only display %d processes", | ||||
gstate->max_topn); | |||||
} | } | ||||
if (ps.jail == 0) { | |||||
ps.jail = 1; | #ifdef ENABLE_COLOR | ||||
new_message(MT_standout | | /* producing a list of color tags is easy */ | ||||
MT_delayed, " Displaying jail " | if (gstate->show_tags) | ||||
"ID."); | |||||
header_text = | |||||
format_header(uname_field); | |||||
reset_display(); | |||||
} | |||||
putchar('\r'); | |||||
} | |||||
else | |||||
{ | { | ||||
clear_message(); | color_dump(stdout); | ||||
exit(EX_OK); | |||||
} | } | ||||
break; | |||||
case CMD_kidletog: | |||||
ps.kidle = !ps.kidle; | |||||
new_message(MT_standout | MT_delayed, | |||||
" %sisplaying system idle process.", | |||||
ps.kidle ? "D" : "Not d"); | |||||
putchar('\r'); | |||||
break; | |||||
case CMD_pcputog: | |||||
pcpu_stats = !pcpu_stats; | |||||
new_message(MT_standout | MT_delayed, | |||||
" Displaying %sCPU statistics.", | |||||
pcpu_stats ? "per-" : "global "); | |||||
toggle_pcpustats(); | |||||
max_topn = display_updatecpus(&statics); | |||||
reset_display(); | |||||
putchar('\r'); | |||||
break; | |||||
default: | |||||
new_message(MT_standout, " BAD CASE IN SWITCH!"); | |||||
putchar('\r'); | |||||
} | |||||
} | |||||
/* flush out stuff that may have been written */ | |||||
fflush(stdout); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#ifdef DEBUG | |||||
fclose(debug); | |||||
#endif | #endif | ||||
quit(0); | |||||
/*NOTREACHED*/ | |||||
} | |||||
/* | /* hold all signals while we initialize the screen */ | ||||
* reset_display() - reset all the display routine pointers so that entire | mask = hold_signals(); | ||||
* screen will get redrawn. | screen_init(); | ||||
*/ | |||||
void | /* set the signal handlers */ | ||||
reset_display() | set_signals(); | ||||
/* longjmp re-entry point */ | |||||
/* set the jump buffer for long jumps out of signal handlers */ | |||||
if (setjmp(jmp_int) != 0) | |||||
{ | { | ||||
d_loadave = i_loadave; | /* this is where we end up after processing sigwinch or sigtstp */ | ||||
d_procstates = i_procstates; | |||||
d_cpustates = i_cpustates; | |||||
d_memory = i_memory; | |||||
d_arc = i_arc; | |||||
d_swap = i_swap; | |||||
d_message = i_message; | |||||
d_header = i_header; | |||||
d_process = i_process; | |||||
} | |||||
/* | /* tell display to resize its buffers, and get the new length */ | ||||
* signal handlers | if ((gstate->max_topn = display_resize()) == -1) | ||||
*/ | |||||
sigret_t leave() /* exit under normal conditions -- INT handler */ | |||||
{ | { | ||||
leaveflag = 1; | /* thats bad */ | ||||
quit(EX_OSERR); | |||||
/*NOTREACHED*/ | |||||
} | } | ||||
sigret_t tstop(i) /* SIGTSTP handler */ | /* set up for a full redraw, and get the current line count */ | ||||
gstate->fulldraw = Yes; | |||||
int i; | /* 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) | |||||
{ | { | ||||
tstopflag = 1; | struct system_info system_info; | ||||
} | struct timeval timeout; | ||||
#ifdef SIGWINCH | time_mark(&(gstate->now)); | ||||
sigret_t winch(i) /* SIGWINCH handler */ | 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); | |||||
int i; | /* 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)) | |||||
{ | { | ||||
winchflag = 1; | do_display(gstate); | ||||
if (gstate->interactive) | |||||
{ | |||||
if (need_mini) | |||||
{ | |||||
do_minidisplay(gstate); | |||||
need_mini = 0; | |||||
} | } | ||||
#endif | do_command(gstate); | ||||
} | |||||
else | |||||
{ | |||||
do_wait(gstate); | |||||
} | |||||
} | |||||
void quit(status) /* exit under duress */ | /* do one last display */ | ||||
do_display(gstate); | |||||
int status; | quit(EX_OK); | ||||
{ | |||||
end_screen(); | |||||
exit(status); | |||||
/*NOTREACHED*/ | /* NOTREACHED */ | ||||
return 1; /* Keep compiler quiet. */ | |||||
} | } |