top.c revision 117709
124139Sjoergchar *copyright = 224139Sjoerg "Copyright (c) 1984 through 1996, William LeFebvre"; 324139Sjoerg 424139Sjoerg/* 524139Sjoerg * Top users/processes display for Unix 624139Sjoerg * Version 3 724139Sjoerg * 824139Sjoerg * This program may be freely redistributed, 924139Sjoerg * but this entire comment MUST remain intact. 1024139Sjoerg * 1124139Sjoerg * Copyright (c) 1984, 1989, William LeFebvre, Rice University 1289757Sdwmalone * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University 1389757Sdwmalone * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory 1489757Sdwmalone * Copyright (c) 1996, William LeFebvre, Group sys Consulting 1566641Simp * 1666641Simp * $FreeBSD: head/contrib/top/top.c 117709 2003-07-17 23:56:40Z julian $ 1724139Sjoerg */ 1824139Sjoerg 1924139Sjoerg/* 2024139Sjoerg * See the file "Changes" for information on version-to-version changes. 2124139Sjoerg */ 2224139Sjoerg 2324139Sjoerg/* 2424139Sjoerg * This file contains "main" and other high-level routines. 2524139Sjoerg */ 2624139Sjoerg 2724139Sjoerg/* 2824139Sjoerg * The following preprocessor variables, when defined, are used to 2924139Sjoerg * distinguish between different Unix implementations: 3024139Sjoerg * 3124139Sjoerg * SIGHOLD - use SVR4 sighold function when defined 3224139Sjoerg * SIGRELSE - use SVR4 sigrelse function when defined 3324139Sjoerg * FD_SET - macros FD_SET and FD_ZERO are used when defined 3424139Sjoerg */ 3524139Sjoerg 3624139Sjoerg#include "os.h" 3786042Sdwmalone#include <errno.h> 3824139Sjoerg#include <signal.h> 3924139Sjoerg#include <setjmp.h> 4024139Sjoerg#include <ctype.h> 4124139Sjoerg#include <sys/time.h> 4224139Sjoerg 4324139Sjoerg/* includes specific to top */ 4424139Sjoerg#include "display.h" /* interface to display package */ 4524139Sjoerg#include "screen.h" /* interface to screen package */ 4624139Sjoerg#include "top.h" 4724139Sjoerg#include "top.local.h" 4824139Sjoerg#include "boolean.h" 4924139Sjoerg#include "machine.h" 5024139Sjoerg#include "utils.h" 5124139Sjoerg 5224139Sjoerg/* Size of the stdio buffer given to stdout */ 5324139Sjoerg#define Buffersize 2048 5424139Sjoerg 5524139Sjoerg/* The buffer that stdio will use */ 5624139Sjoergchar stdoutbuf[Buffersize]; 5724139Sjoerg 5824139Sjoerg/* build Signal masks */ 5924139Sjoerg#define Smask(s) (1 << ((s) - 1)) 6024139Sjoerg 6124139Sjoerg/* for getopt: */ 6224139Sjoergextern int optind; 6324139Sjoergextern char *optarg; 6424139Sjoerg 6524139Sjoerg/* imported from screen.c */ 6624139Sjoergextern int overstrike; 6724139Sjoerg 6824139Sjoerg/* signal handling routines */ 6924139Sjoergsigret_t leave(); 7024139Sjoergsigret_t onalrm(); 7124139Sjoergsigret_t tstop(); 7224139Sjoerg#ifdef SIGWINCH 7324139Sjoergsigret_t winch(); 7424139Sjoerg#endif 7524139Sjoerg 7681187Skrisvolatile sig_atomic_t leaveflag; 7781187Skrisvolatile sig_atomic_t tstopflag; 7881187Skrisvolatile sig_atomic_t winchflag; 7981187Skris 8024139Sjoerg/* internal routines */ 8124139Sjoergvoid quit(); 8224139Sjoerg 8324139Sjoerg/* values which need to be accessed by signal handlers */ 8424139Sjoergstatic int max_topn; /* maximum displayable processes */ 8524139Sjoerg 8624139Sjoerg/* miscellaneous things */ 8724139Sjoergchar *myname = "top"; 8824139Sjoergjmp_buf jmp_int; 8924139Sjoerg 9024139Sjoerg/* routines that don't return int */ 9124139Sjoerg 9224139Sjoergchar *username(); 9324139Sjoergchar *ctime(); 9424139Sjoergchar *kill_procs(); 9524139Sjoergchar *renice_procs(); 9624139Sjoerg 9724139Sjoerg#ifdef ORDER 9824139Sjoergextern int (*proc_compares[])(); 9924139Sjoerg#else 10024139Sjoergextern int proc_compare(); 10124139Sjoerg#endif 10224139Sjoergtime_t time(); 10324139Sjoerg 10424139Sjoergcaddr_t get_process_info(); 10524139Sjoerg 10624139Sjoerg/* different routines for displaying the user's identification */ 10724139Sjoerg/* (values assigned to get_userid) */ 10824139Sjoergchar *username(); 10924139Sjoergchar *itoa7(); 11024139Sjoerg 11124139Sjoerg/* display routines that need to be predeclared */ 11224139Sjoergint i_loadave(); 11324139Sjoergint u_loadave(); 11424139Sjoergint i_procstates(); 11524139Sjoergint u_procstates(); 11624139Sjoergint i_cpustates(); 11724139Sjoergint u_cpustates(); 11824139Sjoergint i_memory(); 11924139Sjoergint u_memory(); 12024142Sjoergint i_swap(); 12124142Sjoergint u_swap(); 12224139Sjoergint i_message(); 12324139Sjoergint u_message(); 12424139Sjoergint i_header(); 12524139Sjoergint u_header(); 12624139Sjoergint i_process(); 12724139Sjoergint u_process(); 12824139Sjoerg 12924139Sjoerg/* pointers to display routines */ 13024139Sjoergint (*d_loadave)() = i_loadave; 13124139Sjoergint (*d_procstates)() = i_procstates; 13224139Sjoergint (*d_cpustates)() = i_cpustates; 13324139Sjoergint (*d_memory)() = i_memory; 13424142Sjoergint (*d_swap)() = i_swap; 13524139Sjoergint (*d_message)() = i_message; 13624139Sjoergint (*d_header)() = i_header; 13724139Sjoergint (*d_process)() = i_process; 13824139Sjoerg 13924139Sjoerg 14024139Sjoergmain(argc, argv) 14124139Sjoerg 14224139Sjoergint argc; 14324139Sjoergchar *argv[]; 14424139Sjoerg 14524139Sjoerg{ 14624139Sjoerg register int i; 14724139Sjoerg register int active_procs; 14824139Sjoerg register int change; 14924139Sjoerg 15024139Sjoerg struct system_info system_info; 15124139Sjoerg struct statics statics; 15224139Sjoerg caddr_t processes; 15324139Sjoerg 15424139Sjoerg static char tempbuf1[50]; 15524139Sjoerg static char tempbuf2[50]; 15624139Sjoerg int old_sigmask; /* only used for BSD-style signals */ 15724139Sjoerg int topn = Default_TOPN; 15824139Sjoerg int delay = Default_DELAY; 15924139Sjoerg int displays = 0; /* indicates unspecified */ 16086042Sdwmalone int sel_ret = 0; 16124139Sjoerg time_t curr_time; 16224139Sjoerg char *(*get_userid)() = username; 16324139Sjoerg char *uname_field = "USERNAME"; 16424139Sjoerg char *header_text; 16524139Sjoerg char *env_top; 16624139Sjoerg char **preset_argv; 16724139Sjoerg int preset_argc = 0; 16824139Sjoerg char **av; 16924139Sjoerg int ac; 17024139Sjoerg char dostates = No; 17124139Sjoerg char do_unames = Yes; 17224139Sjoerg char interactive = Maybe; 17324139Sjoerg char warnings = 0; 17424139Sjoerg#if Default_TOPN == Infinity 17524139Sjoerg char topn_specified = No; 17624139Sjoerg#endif 17724139Sjoerg char ch; 17824139Sjoerg char *iptr; 17924139Sjoerg char no_command = 1; 18024139Sjoerg struct timeval timeout; 18124139Sjoerg struct process_select ps; 18224139Sjoerg#ifdef ORDER 18324139Sjoerg char *order_name = NULL; 18424139Sjoerg int order_index = 0; 18524139Sjoerg#endif 18624139Sjoerg#ifndef FD_SET 18724139Sjoerg /* FD_SET and friends are not present: fake it */ 18824139Sjoerg typedef int fd_set; 18924139Sjoerg#define FD_ZERO(x) (*(x) = 0) 19089757Sdwmalone#define FD_SET(f, x) (*(x) = 1<<f) 19124139Sjoerg#endif 19224139Sjoerg fd_set readfds; 19324139Sjoerg 19424139Sjoerg#ifdef ORDER 195117709Sjulian static char command_chars[] = "\f qh?en#sdkriIutHo"; 19624139Sjoerg#else 197117709Sjulian static char command_chars[] = "\f qh?en#sdkriIutH"; 19824139Sjoerg#endif 19924139Sjoerg/* these defines enumerate the "strchr"s of the commands in command_chars */ 20024139Sjoerg#define CMD_redraw 0 20124139Sjoerg#define CMD_update 1 20224139Sjoerg#define CMD_quit 2 20324139Sjoerg#define CMD_help1 3 20424139Sjoerg#define CMD_help2 4 20524139Sjoerg#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */ 20624139Sjoerg#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */ 20724139Sjoerg#define CMD_number1 6 20824139Sjoerg#define CMD_number2 7 20924139Sjoerg#define CMD_delay 8 21024139Sjoerg#define CMD_displays 9 21124139Sjoerg#define CMD_kill 10 21224139Sjoerg#define CMD_renice 11 21324139Sjoerg#define CMD_idletog 12 21424139Sjoerg#define CMD_idletog2 13 21524139Sjoerg#define CMD_user 14 21638090Sdes#define CMD_selftog 15 217117709Sjulian#define CMD_thrtog 16 21824139Sjoerg#ifdef ORDER 219117709Sjulian#define CMD_order 17 22024139Sjoerg#endif 22124139Sjoerg 22224139Sjoerg /* set the buffer for stdout */ 22324139Sjoerg#ifdef DEBUG 22489757Sdwmalone extern FILE *debug; 22589757Sdwmalone debug = fopen("debug.run", "w"); 22624139Sjoerg setbuffer(stdout, NULL, 0); 22724139Sjoerg#else 22824139Sjoerg setbuffer(stdout, stdoutbuf, Buffersize); 22924139Sjoerg#endif 23024139Sjoerg 23124139Sjoerg /* get our name */ 23224139Sjoerg if (argc > 0) 23324139Sjoerg { 23424139Sjoerg if ((myname = strrchr(argv[0], '/')) == 0) 23524139Sjoerg { 23624139Sjoerg myname = argv[0]; 23724139Sjoerg } 23824139Sjoerg else 23924139Sjoerg { 24024139Sjoerg myname++; 24124139Sjoerg } 24224139Sjoerg } 24324139Sjoerg 24424139Sjoerg /* initialize some selection options */ 24524139Sjoerg ps.idle = Yes; 24638090Sdes ps.self = -1; 24724139Sjoerg ps.system = No; 24824139Sjoerg ps.uid = -1; 249117709Sjulian ps.thread = No; 25024139Sjoerg ps.command = NULL; 25124139Sjoerg 25224139Sjoerg /* get preset options from the environment */ 25324139Sjoerg if ((env_top = getenv("TOP")) != NULL) 25424139Sjoerg { 25524139Sjoerg av = preset_argv = argparse(env_top, &preset_argc); 25624139Sjoerg ac = preset_argc; 25724139Sjoerg 25824139Sjoerg /* set the dummy argument to an explanatory message, in case 25924139Sjoerg getopt encounters a bad argument */ 26024139Sjoerg preset_argv[0] = "while processing environment"; 26124139Sjoerg } 26224139Sjoerg 26324139Sjoerg /* process options */ 26424139Sjoerg do { 26524139Sjoerg /* if we're done doing the presets, then process the real arguments */ 26624139Sjoerg if (preset_argc == 0) 26724139Sjoerg { 26824139Sjoerg ac = argc; 26924139Sjoerg av = argv; 27024139Sjoerg 27124139Sjoerg /* this should keep getopt happy... */ 27224139Sjoerg optind = 1; 27324139Sjoerg } 27424139Sjoerg 275117709Sjulian while ((i = getopt(ac, av, "SIHbinquvs:d:U:o:t")) != EOF) 27624139Sjoerg { 27724139Sjoerg switch(i) 27824139Sjoerg { 27989757Sdwmalone case 'v': /* show version number */ 28089757Sdwmalone fprintf(stderr, "%s: version %s\n", 28189757Sdwmalone myname, version_string()); 28289757Sdwmalone exit(1); 28389757Sdwmalone break; 28489757Sdwmalone 28524139Sjoerg case 'u': /* toggle uid/username display */ 28624139Sjoerg do_unames = !do_unames; 28724139Sjoerg break; 28824139Sjoerg 28924139Sjoerg case 'U': /* display only username's processes */ 29024139Sjoerg if ((ps.uid = userid(optarg)) == -1) 29124139Sjoerg { 29224139Sjoerg fprintf(stderr, "%s: unknown user\n", optarg); 29324139Sjoerg exit(1); 29424139Sjoerg } 29524139Sjoerg break; 29624139Sjoerg 29724139Sjoerg case 'S': /* show system processes */ 29824139Sjoerg ps.system = !ps.system; 29924139Sjoerg break; 30024139Sjoerg 30124139Sjoerg case 'I': /* show idle processes */ 30224139Sjoerg ps.idle = !ps.idle; 30324139Sjoerg break; 30424139Sjoerg 30524139Sjoerg case 'i': /* go interactive regardless */ 30624139Sjoerg interactive = Yes; 30724139Sjoerg break; 30824139Sjoerg 30924139Sjoerg case 'n': /* batch, or non-interactive */ 31024139Sjoerg case 'b': 31124139Sjoerg interactive = No; 31224139Sjoerg break; 31324139Sjoerg 31424139Sjoerg case 'd': /* number of displays to show */ 31524139Sjoerg if ((i = atoiwi(optarg)) == Invalid || i == 0) 31624139Sjoerg { 31724139Sjoerg fprintf(stderr, 31824139Sjoerg "%s: warning: display count should be positive -- option ignored\n", 31924139Sjoerg myname); 32024139Sjoerg warnings++; 32124139Sjoerg } 32224139Sjoerg else 32324139Sjoerg { 32424139Sjoerg displays = i; 32524139Sjoerg } 32624139Sjoerg break; 32724139Sjoerg 32824139Sjoerg case 's': 32989757Sdwmalone if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) 33024139Sjoerg { 33124139Sjoerg fprintf(stderr, 33289757Sdwmalone "%s: warning: seconds delay should be positive -- using default\n", 33324139Sjoerg myname); 33424139Sjoerg delay = Default_DELAY; 33524139Sjoerg warnings++; 33624139Sjoerg } 33724139Sjoerg break; 33824139Sjoerg 33924139Sjoerg case 'q': /* be quick about it */ 34024139Sjoerg /* only allow this if user is really root */ 34124139Sjoerg if (getuid() == 0) 34224139Sjoerg { 34324139Sjoerg /* be very un-nice! */ 34424139Sjoerg (void) nice(-20); 34524139Sjoerg } 34624139Sjoerg else 34724139Sjoerg { 34824139Sjoerg fprintf(stderr, 34924139Sjoerg "%s: warning: `-q' option can only be used by root\n", 35024139Sjoerg myname); 35124139Sjoerg warnings++; 35224139Sjoerg } 35324139Sjoerg break; 35424139Sjoerg 35524139Sjoerg case 'o': /* select sort order */ 35624139Sjoerg#ifdef ORDER 35724139Sjoerg order_name = optarg; 35824139Sjoerg#else 35924139Sjoerg fprintf(stderr, 36024139Sjoerg "%s: this platform does not support arbitrary ordering. Sorry.\n", 36124139Sjoerg myname); 36224139Sjoerg warnings++; 36324139Sjoerg#endif 36424139Sjoerg break; 36524139Sjoerg 36638090Sdes case 't': 36738090Sdes ps.self = (ps.self == -1) ? getpid() : -1; 36838090Sdes break; 36938090Sdes 370117709Sjulian case 'H': 371117709Sjulian ps.thread = !ps.thread; 372117709Sjulian break; 373117709Sjulian 37424139Sjoerg default: 37524139Sjoerg fprintf(stderr, "\ 37624139SjoergTop version %s\n\ 377117709SjulianUsage: %s [-HISbinqut] [-d x] [-s x] [-o field] [-U username] [number]\n", 37824139Sjoerg version_string(), myname); 37924139Sjoerg exit(1); 38024139Sjoerg } 38124139Sjoerg } 38224139Sjoerg 38324139Sjoerg /* get count of top processes to display (if any) */ 38424139Sjoerg if (optind < ac) 38524139Sjoerg { 38624139Sjoerg if ((topn = atoiwi(av[optind])) == Invalid) 38724139Sjoerg { 38824139Sjoerg fprintf(stderr, 38924139Sjoerg "%s: warning: process display count should be non-negative -- using default\n", 39024139Sjoerg myname); 39124139Sjoerg warnings++; 39224139Sjoerg } 39324139Sjoerg#if Default_TOPN == Infinity 39424139Sjoerg else 39524139Sjoerg { 39624139Sjoerg topn_specified = Yes; 39724139Sjoerg } 39824139Sjoerg#endif 39924139Sjoerg } 40024139Sjoerg 40124139Sjoerg /* tricky: remember old value of preset_argc & set preset_argc = 0 */ 40224139Sjoerg i = preset_argc; 40324139Sjoerg preset_argc = 0; 40424139Sjoerg 40524139Sjoerg /* repeat only if we really did the preset arguments */ 40624139Sjoerg } while (i != 0); 40724139Sjoerg 40824139Sjoerg /* set constants for username/uid display correctly */ 40924139Sjoerg if (!do_unames) 41024139Sjoerg { 41124139Sjoerg uname_field = " UID "; 41224139Sjoerg get_userid = itoa7; 41324139Sjoerg } 41424139Sjoerg 41524139Sjoerg /* initialize the kernel memory interface */ 41624139Sjoerg if (machine_init(&statics) == -1) 41724139Sjoerg { 41824139Sjoerg exit(1); 41924139Sjoerg } 42024139Sjoerg 42124139Sjoerg#ifdef ORDER 42224139Sjoerg /* determine sorting order index, if necessary */ 42324139Sjoerg if (order_name != NULL) 42424139Sjoerg { 42524139Sjoerg if ((order_index = string_index(order_name, statics.order_names)) == -1) 42624139Sjoerg { 42724139Sjoerg char **pp; 42824139Sjoerg 42924139Sjoerg fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", 43024139Sjoerg myname, order_name); 43124139Sjoerg fprintf(stderr, "\tTry one of these:"); 43224139Sjoerg pp = statics.order_names; 43324139Sjoerg while (*pp != NULL) 43424139Sjoerg { 43524139Sjoerg fprintf(stderr, " %s", *pp++); 43624139Sjoerg } 43724139Sjoerg fputc('\n', stderr); 43824139Sjoerg exit(1); 43924139Sjoerg } 44024139Sjoerg } 44124139Sjoerg#endif 44224139Sjoerg 44324139Sjoerg#ifdef no_initialization_needed 44424139Sjoerg /* initialize the hashing stuff */ 44524139Sjoerg if (do_unames) 44624139Sjoerg { 44724139Sjoerg init_hash(); 44824139Sjoerg } 44924139Sjoerg#endif 45024139Sjoerg 45124139Sjoerg /* initialize termcap */ 45224139Sjoerg init_termcap(interactive); 45324139Sjoerg 45424139Sjoerg /* get the string to use for the process area header */ 45524139Sjoerg header_text = format_header(uname_field); 45624139Sjoerg 45724139Sjoerg /* initialize display interface */ 45824139Sjoerg if ((max_topn = display_init(&statics)) == -1) 45924139Sjoerg { 46024139Sjoerg fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); 46124139Sjoerg exit(4); 46224139Sjoerg } 46324139Sjoerg 46424139Sjoerg /* print warning if user requested more processes than we can display */ 46524139Sjoerg if (topn > max_topn) 46624139Sjoerg { 46724139Sjoerg fprintf(stderr, 46824139Sjoerg "%s: warning: this terminal can only display %d processes.\n", 46924139Sjoerg myname, max_topn); 47024139Sjoerg warnings++; 47124139Sjoerg } 47224139Sjoerg 47324139Sjoerg /* adjust for topn == Infinity */ 47424139Sjoerg if (topn == Infinity) 47524139Sjoerg { 47624139Sjoerg /* 47724139Sjoerg * For smart terminals, infinity really means everything that can 47824139Sjoerg * be displayed, or Largest. 47924139Sjoerg * On dumb terminals, infinity means every process in the system! 48024139Sjoerg * We only really want to do that if it was explicitly specified. 48124139Sjoerg * This is always the case when "Default_TOPN != Infinity". But if 48224139Sjoerg * topn wasn't explicitly specified and we are on a dumb terminal 48324139Sjoerg * and the default is Infinity, then (and only then) we use 48424139Sjoerg * "Nominal_TOPN" instead. 48524139Sjoerg */ 48624139Sjoerg#if Default_TOPN == Infinity 48724139Sjoerg topn = smart_terminal ? Largest : 48824139Sjoerg (topn_specified ? Largest : Nominal_TOPN); 48924139Sjoerg#else 49024139Sjoerg topn = Largest; 49124139Sjoerg#endif 49224139Sjoerg } 49324139Sjoerg 49424139Sjoerg /* set header display accordingly */ 49524139Sjoerg display_header(topn > 0); 49624139Sjoerg 49724139Sjoerg /* determine interactive state */ 49824139Sjoerg if (interactive == Maybe) 49924139Sjoerg { 50024139Sjoerg interactive = smart_terminal; 50124139Sjoerg } 50224139Sjoerg 50324139Sjoerg /* if # of displays not specified, fill it in */ 50424139Sjoerg if (displays == 0) 50524139Sjoerg { 50624139Sjoerg displays = smart_terminal ? Infinity : 1; 50724139Sjoerg } 50824139Sjoerg 50924139Sjoerg /* hold interrupt signals while setting up the screen and the handlers */ 51024139Sjoerg#ifdef SIGHOLD 51124139Sjoerg sighold(SIGINT); 51224139Sjoerg sighold(SIGQUIT); 51324139Sjoerg sighold(SIGTSTP); 51424139Sjoerg#else 51524139Sjoerg old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); 51624139Sjoerg#endif 51724139Sjoerg init_screen(); 51824139Sjoerg (void) signal(SIGINT, leave); 51924139Sjoerg (void) signal(SIGQUIT, leave); 52024139Sjoerg (void) signal(SIGTSTP, tstop); 52124139Sjoerg#ifdef SIGWINCH 52224139Sjoerg (void) signal(SIGWINCH, winch); 52324139Sjoerg#endif 52424139Sjoerg#ifdef SIGRELSE 52524139Sjoerg sigrelse(SIGINT); 52624139Sjoerg sigrelse(SIGQUIT); 52724139Sjoerg sigrelse(SIGTSTP); 52824139Sjoerg#else 52924139Sjoerg (void) sigsetmask(old_sigmask); 53024139Sjoerg#endif 53124139Sjoerg if (warnings) 53224139Sjoerg { 53324139Sjoerg fputs("....", stderr); 53424139Sjoerg fflush(stderr); /* why must I do this? */ 53524139Sjoerg sleep((unsigned)(3 * warnings)); 53624139Sjoerg fputc('\n', stderr); 53724139Sjoerg } 53824139Sjoerg 53981187Skrisrestart: 54024139Sjoerg 54124139Sjoerg /* 54224139Sjoerg * main loop -- repeat while display count is positive or while it 54324139Sjoerg * indicates infinity (by being -1) 54424139Sjoerg */ 54524139Sjoerg 54624139Sjoerg while ((displays == -1) || (displays-- > 0)) 54724139Sjoerg { 54824139Sjoerg /* get the current stats */ 54924139Sjoerg get_system_info(&system_info); 55024139Sjoerg 55124139Sjoerg /* get the current set of processes */ 55224139Sjoerg processes = 55324139Sjoerg get_process_info(&system_info, 55424139Sjoerg &ps, 55524139Sjoerg#ifdef ORDER 55624139Sjoerg proc_compares[order_index]); 55724139Sjoerg#else 55824139Sjoerg proc_compare); 55924139Sjoerg#endif 56024139Sjoerg 56124139Sjoerg /* display the load averages */ 56224139Sjoerg (*d_loadave)(system_info.last_pid, 56324139Sjoerg system_info.load_avg); 56424139Sjoerg 56524139Sjoerg /* display the current time */ 56624139Sjoerg /* this method of getting the time SHOULD be fairly portable */ 56724139Sjoerg time(&curr_time); 56842447Sobrien i_uptime(&system_info.boottime, &curr_time); 56924139Sjoerg i_timeofday(&curr_time); 57024139Sjoerg 57124139Sjoerg /* display process state breakdown */ 57224139Sjoerg (*d_procstates)(system_info.p_total, 57324139Sjoerg system_info.procstates); 57424139Sjoerg 57524139Sjoerg /* display the cpu state percentage breakdown */ 57624139Sjoerg if (dostates) /* but not the first time */ 57724139Sjoerg { 57824139Sjoerg (*d_cpustates)(system_info.cpustates); 57924139Sjoerg } 58024139Sjoerg else 58124139Sjoerg { 58224139Sjoerg /* we'll do it next time */ 58324139Sjoerg if (smart_terminal) 58424139Sjoerg { 58524139Sjoerg z_cpustates(); 58624139Sjoerg } 58724139Sjoerg else 58824139Sjoerg { 58924139Sjoerg putchar('\n'); 59024139Sjoerg } 59124139Sjoerg dostates = Yes; 59224139Sjoerg } 59324139Sjoerg 59424139Sjoerg /* display memory stats */ 59524139Sjoerg (*d_memory)(system_info.memory); 59624139Sjoerg 59724142Sjoerg /* display swap stats */ 59824142Sjoerg (*d_swap)(system_info.swap); 59924142Sjoerg 60024139Sjoerg /* handle message area */ 60124139Sjoerg (*d_message)(); 60224139Sjoerg 60324139Sjoerg /* update the header area */ 60424139Sjoerg (*d_header)(header_text); 60524139Sjoerg 60624139Sjoerg if (topn > 0) 60724139Sjoerg { 60824139Sjoerg /* determine number of processes to actually display */ 60924139Sjoerg /* this number will be the smallest of: active processes, 61024139Sjoerg number user requested, number current screen accomodates */ 61189757Sdwmalone active_procs = system_info.P_ACTIVE; 61224139Sjoerg if (active_procs > topn) 61324139Sjoerg { 61424139Sjoerg active_procs = topn; 61524139Sjoerg } 61624139Sjoerg if (active_procs > max_topn) 61724139Sjoerg { 61824139Sjoerg active_procs = max_topn; 61924139Sjoerg } 62024139Sjoerg 62124139Sjoerg /* now show the top "n" processes. */ 62224139Sjoerg for (i = 0; i < active_procs; i++) 62324139Sjoerg { 62424139Sjoerg (*d_process)(i, format_next_process(processes, get_userid)); 62524139Sjoerg } 62624139Sjoerg } 62724139Sjoerg else 62824139Sjoerg { 62924139Sjoerg i = 0; 63024139Sjoerg } 63124139Sjoerg 63224139Sjoerg /* do end-screen processing */ 63324139Sjoerg u_endscreen(i); 63424139Sjoerg 63524139Sjoerg /* now, flush the output buffer */ 63689757Sdwmalone if (fflush(stdout) != 0) 63789757Sdwmalone { 63889757Sdwmalone new_message(MT_standout, " Write error on stdout"); 63989757Sdwmalone putchar('\r'); 64089757Sdwmalone quit(1); 64189757Sdwmalone /*NOTREACHED*/ 64289757Sdwmalone } 64324139Sjoerg 64424139Sjoerg /* only do the rest if we have more displays to show */ 64524139Sjoerg if (displays) 64624139Sjoerg { 64724139Sjoerg /* switch out for new display on smart terminals */ 64824139Sjoerg if (smart_terminal) 64924139Sjoerg { 65024139Sjoerg if (overstrike) 65124139Sjoerg { 65224139Sjoerg reset_display(); 65324139Sjoerg } 65424139Sjoerg else 65524139Sjoerg { 65624139Sjoerg d_loadave = u_loadave; 65724139Sjoerg d_procstates = u_procstates; 65824139Sjoerg d_cpustates = u_cpustates; 65924139Sjoerg d_memory = u_memory; 66024142Sjoerg d_swap = u_swap; 66124139Sjoerg d_message = u_message; 66224139Sjoerg d_header = u_header; 66324139Sjoerg d_process = u_process; 66424139Sjoerg } 66524139Sjoerg } 66624139Sjoerg 66724139Sjoerg no_command = Yes; 66824139Sjoerg if (!interactive) 66924139Sjoerg { 67024139Sjoerg /* set up alarm */ 67124139Sjoerg (void) signal(SIGALRM, onalrm); 67224139Sjoerg (void) alarm((unsigned)delay); 67324139Sjoerg 67424139Sjoerg /* wait for the rest of it .... */ 67524139Sjoerg pause(); 67624139Sjoerg } 67724139Sjoerg else while (no_command) 67824139Sjoerg { 67924139Sjoerg /* assume valid command unless told otherwise */ 68024139Sjoerg no_command = No; 68124139Sjoerg 68224139Sjoerg /* set up arguments for select with timeout */ 68324139Sjoerg FD_ZERO(&readfds); 68489757Sdwmalone FD_SET(0, &readfds); /* for standard input */ 68524139Sjoerg timeout.tv_sec = delay; 68624139Sjoerg timeout.tv_usec = 0; 68724139Sjoerg 68881187Skris if (leaveflag) { 68981187Skris end_screen(); 69081187Skris exit(0); 69181187Skris } 69281187Skris 69381187Skris if (tstopflag) { 69481187Skris /* move to the lower left */ 69581187Skris end_screen(); 69681187Skris fflush(stdout); 69781187Skris 69881187Skris /* default the signal handler action */ 69981187Skris (void) signal(SIGTSTP, SIG_DFL); 70081187Skris 70181187Skris /* unblock the signal and send ourselves one */ 70281187Skris#ifdef SIGRELSE 70381187Skris sigrelse(SIGTSTP); 70481187Skris#else 70581187Skris (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); 70681187Skris#endif 70781187Skris (void) kill(0, SIGTSTP); 70881187Skris 70981187Skris /* reset the signal handler */ 71081187Skris (void) signal(SIGTSTP, tstop); 71181187Skris 71281187Skris /* reinit screen */ 71381187Skris reinit_screen(); 71481187Skris reset_display(); 71581187Skris tstopflag = 0; 71681187Skris goto restart; 71781187Skris } 71881187Skris 71981187Skris if (winchflag) { 72081187Skris /* reascertain the screen dimensions */ 72181187Skris get_screensize(); 72281187Skris 72381187Skris /* tell display to resize */ 72481187Skris max_topn = display_resize(); 72581187Skris 72681187Skris /* reset the signal handler */ 72781187Skris (void) signal(SIGWINCH, winch); 72881187Skris 72981187Skris reset_display(); 73081187Skris winchflag = 0; 73181187Skris goto restart; 73281187Skris } 73381187Skris 73424139Sjoerg /* wait for either input or the end of the delay period */ 73586042Sdwmalone sel_ret = select(2, &readfds, NULL, NULL, &timeout); 73686042Sdwmalone if (sel_ret < 0 && errno != EINTR) 73786042Sdwmalone quit(0); 73886042Sdwmalone if (sel_ret > 0) 73924139Sjoerg { 74024139Sjoerg int newval; 74124139Sjoerg char *errmsg; 74224139Sjoerg 74324139Sjoerg /* something to read -- clear the message area first */ 74424139Sjoerg clear_message(); 74524139Sjoerg 74624139Sjoerg /* now read it and convert to command strchr */ 74724139Sjoerg /* (use "change" as a temporary to hold strchr) */ 74886042Sdwmalone if (read(0, &ch, 1) != 1) 74989757Sdwmalone { 75089757Sdwmalone /* read error: either 0 or -1 */ 75189757Sdwmalone new_message(MT_standout, " Read error on stdin"); 75289757Sdwmalone putchar('\r'); 75389757Sdwmalone quit(1); 75489757Sdwmalone /*NOTREACHED*/ 75589757Sdwmalone } 75624139Sjoerg if ((iptr = strchr(command_chars, ch)) == NULL) 75724139Sjoerg { 75824142Sjoerg if (ch != '\r' && ch != '\n') 75924142Sjoerg { 76024142Sjoerg /* illegal command */ 76124142Sjoerg new_message(MT_standout, " Command not understood"); 76224142Sjoerg } 76324139Sjoerg putchar('\r'); 76424139Sjoerg no_command = Yes; 76524139Sjoerg } 76624139Sjoerg else 76724139Sjoerg { 76824139Sjoerg change = iptr - command_chars; 76924139Sjoerg if (overstrike && change > CMD_OSLIMIT) 77024139Sjoerg { 77124139Sjoerg /* error */ 77224139Sjoerg new_message(MT_standout, 77324139Sjoerg " Command cannot be handled by this terminal"); 77424139Sjoerg putchar('\r'); 77524139Sjoerg no_command = Yes; 77624139Sjoerg } 77724139Sjoerg else switch(change) 77824139Sjoerg { 77924139Sjoerg case CMD_redraw: /* redraw screen */ 78024139Sjoerg reset_display(); 78124139Sjoerg break; 78224139Sjoerg 78324139Sjoerg case CMD_update: /* merely update display */ 78424139Sjoerg /* is the load average high? */ 78524139Sjoerg if (system_info.load_avg[0] > LoadMax) 78624139Sjoerg { 78724139Sjoerg /* yes, go home for visual feedback */ 78824139Sjoerg go_home(); 78924139Sjoerg fflush(stdout); 79024139Sjoerg } 79124139Sjoerg break; 79224139Sjoerg 79324139Sjoerg case CMD_quit: /* quit */ 79424139Sjoerg quit(0); 79524139Sjoerg /*NOTREACHED*/ 79624139Sjoerg break; 79724139Sjoerg 79824139Sjoerg case CMD_help1: /* help */ 79924139Sjoerg case CMD_help2: 80024139Sjoerg reset_display(); 80124139Sjoerg clear(); 80224139Sjoerg show_help(); 80324139Sjoerg standout("Hit any key to continue: "); 80424139Sjoerg fflush(stdout); 80524139Sjoerg (void) read(0, &ch, 1); 80624139Sjoerg break; 80724139Sjoerg 80824139Sjoerg case CMD_errors: /* show errors */ 80924139Sjoerg if (error_count() == 0) 81024139Sjoerg { 81124139Sjoerg new_message(MT_standout, 81224139Sjoerg " Currently no errors to report."); 81324139Sjoerg putchar('\r'); 81424139Sjoerg no_command = Yes; 81524139Sjoerg } 81624139Sjoerg else 81724139Sjoerg { 81824139Sjoerg reset_display(); 81924139Sjoerg clear(); 82024139Sjoerg show_errors(); 82124139Sjoerg standout("Hit any key to continue: "); 82224139Sjoerg fflush(stdout); 82324139Sjoerg (void) read(0, &ch, 1); 82424139Sjoerg } 82524139Sjoerg break; 82624139Sjoerg 82724139Sjoerg case CMD_number1: /* new number */ 82824139Sjoerg case CMD_number2: 82924139Sjoerg new_message(MT_standout, 83024139Sjoerg "Number of processes to show: "); 83124139Sjoerg newval = readline(tempbuf1, 8, Yes); 83224139Sjoerg if (newval > -1) 83324139Sjoerg { 83424139Sjoerg if (newval > max_topn) 83524139Sjoerg { 83624139Sjoerg new_message(MT_standout | MT_delayed, 83724139Sjoerg " This terminal can only display %d processes.", 83824139Sjoerg max_topn); 83924139Sjoerg putchar('\r'); 84024139Sjoerg } 84124139Sjoerg 84224139Sjoerg if (newval == 0) 84324139Sjoerg { 84424139Sjoerg /* inhibit the header */ 84524139Sjoerg display_header(No); 84624139Sjoerg } 84724139Sjoerg else if (newval > topn && topn == 0) 84824139Sjoerg { 84924139Sjoerg /* redraw the header */ 85024139Sjoerg display_header(Yes); 85124139Sjoerg d_header = i_header; 85224139Sjoerg } 85324139Sjoerg topn = newval; 85424139Sjoerg } 85524139Sjoerg break; 85624139Sjoerg 85724139Sjoerg case CMD_delay: /* new seconds delay */ 85824139Sjoerg new_message(MT_standout, "Seconds to delay: "); 85924139Sjoerg if ((i = readline(tempbuf1, 8, Yes)) > -1) 86024139Sjoerg { 86189757Sdwmalone if ((delay = i) == 0 && getuid() != 0) 86289757Sdwmalone { 86389757Sdwmalone delay = 1; 86489757Sdwmalone } 86524139Sjoerg } 86624139Sjoerg clear_message(); 86724139Sjoerg break; 86824139Sjoerg 86924139Sjoerg case CMD_displays: /* change display count */ 87024139Sjoerg new_message(MT_standout, 87124139Sjoerg "Displays to show (currently %s): ", 87224139Sjoerg displays == -1 ? "infinite" : 87324139Sjoerg itoa(displays)); 87424139Sjoerg if ((i = readline(tempbuf1, 10, Yes)) > 0) 87524139Sjoerg { 87624139Sjoerg displays = i; 87724139Sjoerg } 87824139Sjoerg else if (i == 0) 87924139Sjoerg { 88024139Sjoerg quit(0); 88124139Sjoerg } 88224139Sjoerg clear_message(); 88324139Sjoerg break; 88424139Sjoerg 88524139Sjoerg case CMD_kill: /* kill program */ 88624139Sjoerg new_message(0, "kill "); 88724139Sjoerg if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 88824139Sjoerg { 88924139Sjoerg if ((errmsg = kill_procs(tempbuf2)) != NULL) 89024139Sjoerg { 89166641Simp new_message(MT_standout, "%s", errmsg); 89224139Sjoerg putchar('\r'); 89324139Sjoerg no_command = Yes; 89424139Sjoerg } 89524139Sjoerg } 89624139Sjoerg else 89724139Sjoerg { 89824139Sjoerg clear_message(); 89924139Sjoerg } 90024139Sjoerg break; 90124139Sjoerg 90224139Sjoerg case CMD_renice: /* renice program */ 90324139Sjoerg new_message(0, "renice "); 90424139Sjoerg if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 90524139Sjoerg { 90624139Sjoerg if ((errmsg = renice_procs(tempbuf2)) != NULL) 90724139Sjoerg { 90868293Simp new_message(MT_standout, "%s", errmsg); 90924139Sjoerg putchar('\r'); 91024139Sjoerg no_command = Yes; 91124139Sjoerg } 91224139Sjoerg } 91324139Sjoerg else 91424139Sjoerg { 91524139Sjoerg clear_message(); 91624139Sjoerg } 91724139Sjoerg break; 91824139Sjoerg 91924139Sjoerg case CMD_idletog: 92024139Sjoerg case CMD_idletog2: 92124139Sjoerg ps.idle = !ps.idle; 92224139Sjoerg new_message(MT_standout | MT_delayed, 92324139Sjoerg " %sisplaying idle processes.", 92424139Sjoerg ps.idle ? "D" : "Not d"); 92524139Sjoerg putchar('\r'); 92624139Sjoerg break; 92724139Sjoerg 92838090Sdes case CMD_selftog: 92938090Sdes ps.self = (ps.self == -1) ? getpid() : -1; 93038090Sdes new_message(MT_standout | MT_delayed, 93138090Sdes " %sisplaying self.", 93238090Sdes (ps.self == -1) ? "D" : "Not d"); 93338090Sdes putchar('\r'); 93438090Sdes break; 93538090Sdes 93624139Sjoerg case CMD_user: 93724139Sjoerg new_message(MT_standout, 93824139Sjoerg "Username to show: "); 93924139Sjoerg if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 94024139Sjoerg { 94124139Sjoerg if (tempbuf2[0] == '+' && 94224139Sjoerg tempbuf2[1] == '\0') 94324139Sjoerg { 94424139Sjoerg ps.uid = -1; 94524139Sjoerg } 94624139Sjoerg else if ((i = userid(tempbuf2)) == -1) 94724139Sjoerg { 94824139Sjoerg new_message(MT_standout, 94924139Sjoerg " %s: unknown user", tempbuf2); 95024139Sjoerg no_command = Yes; 95124139Sjoerg } 95224139Sjoerg else 95324139Sjoerg { 95424139Sjoerg ps.uid = i; 95524139Sjoerg } 95624139Sjoerg putchar('\r'); 95724139Sjoerg } 95824139Sjoerg else 95924139Sjoerg { 96024139Sjoerg clear_message(); 96124139Sjoerg } 96224139Sjoerg break; 96324139Sjoerg 964117709Sjulian case CMD_thrtog: 965117709Sjulian ps.thread = !ps.thread; 966117709Sjulian new_message(MT_standout | MT_delayed, 967117709Sjulian " %sisplaying threads.", 968117709Sjulian ps.thread ? "D" : "Not d"); 969117709Sjulian putchar('\r'); 970117709Sjulian break; 97124139Sjoerg#ifdef ORDER 97224139Sjoerg case CMD_order: 97324139Sjoerg new_message(MT_standout, 97424139Sjoerg "Order to sort: "); 97524139Sjoerg if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 97624139Sjoerg { 97724139Sjoerg if ((i = string_index(tempbuf2, statics.order_names)) == -1) 97824139Sjoerg { 97924139Sjoerg new_message(MT_standout, 98024139Sjoerg " %s: unrecognized sorting order", tempbuf2); 98124139Sjoerg no_command = Yes; 98224139Sjoerg } 98324139Sjoerg else 98424139Sjoerg { 98524139Sjoerg order_index = i; 98624139Sjoerg } 98724139Sjoerg putchar('\r'); 98824139Sjoerg } 98924139Sjoerg else 99024139Sjoerg { 99124139Sjoerg clear_message(); 99224139Sjoerg } 99324139Sjoerg break; 99424139Sjoerg#endif 99524139Sjoerg 99624139Sjoerg default: 99724139Sjoerg new_message(MT_standout, " BAD CASE IN SWITCH!"); 99824139Sjoerg putchar('\r'); 99924139Sjoerg } 100024139Sjoerg } 100124139Sjoerg 100224139Sjoerg /* flush out stuff that may have been written */ 100324139Sjoerg fflush(stdout); 100424139Sjoerg } 100524139Sjoerg } 100624139Sjoerg } 100724139Sjoerg } 100824139Sjoerg 100989757Sdwmalone#ifdef DEBUG 101089757Sdwmalone fclose(debug); 101189757Sdwmalone#endif 101224139Sjoerg quit(0); 101324139Sjoerg /*NOTREACHED*/ 101424139Sjoerg} 101524139Sjoerg 101624139Sjoerg/* 101724139Sjoerg * reset_display() - reset all the display routine pointers so that entire 101824139Sjoerg * screen will get redrawn. 101924139Sjoerg */ 102024139Sjoerg 102124139Sjoergreset_display() 102224139Sjoerg 102324139Sjoerg{ 102424139Sjoerg d_loadave = i_loadave; 102524139Sjoerg d_procstates = i_procstates; 102624139Sjoerg d_cpustates = i_cpustates; 102724139Sjoerg d_memory = i_memory; 102824142Sjoerg d_swap = i_swap; 102924139Sjoerg d_message = i_message; 103024139Sjoerg d_header = i_header; 103124139Sjoerg d_process = i_process; 103224139Sjoerg} 103324139Sjoerg 103424139Sjoerg/* 103524139Sjoerg * signal handlers 103624139Sjoerg */ 103724139Sjoerg 103824139Sjoergsigret_t leave() /* exit under normal conditions -- INT handler */ 103924139Sjoerg 104024139Sjoerg{ 104181187Skris leaveflag = 1; 104224139Sjoerg} 104324139Sjoerg 104424139Sjoergsigret_t tstop(i) /* SIGTSTP handler */ 104524139Sjoerg 104624139Sjoergint i; 104724139Sjoerg 104824139Sjoerg{ 104981187Skris tstopflag = 1; 105024139Sjoerg} 105124139Sjoerg 105224139Sjoerg#ifdef SIGWINCH 105324139Sjoergsigret_t winch(i) /* SIGWINCH handler */ 105424139Sjoerg 105524139Sjoergint i; 105624139Sjoerg 105724139Sjoerg{ 105881187Skris winchflag = 1; 105924139Sjoerg} 106024139Sjoerg#endif 106124139Sjoerg 106224139Sjoergvoid quit(status) /* exit under duress */ 106324139Sjoerg 106424139Sjoergint status; 106524139Sjoerg 106624139Sjoerg{ 106724139Sjoerg end_screen(); 106824139Sjoerg exit(status); 106924139Sjoerg /*NOTREACHED*/ 107024139Sjoerg} 107124139Sjoerg 107224139Sjoergsigret_t onalrm() /* SIGALRM handler */ 107324139Sjoerg 107424139Sjoerg{ 107524139Sjoerg /* this is only used in batch mode to break out of the pause() */ 107624139Sjoerg /* return; */ 107724139Sjoerg} 107824139Sjoerg 1079