top.c revision 24142
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
1224139Sjoerg *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1324139Sjoerg */
1424139Sjoerg
1524139Sjoerg/*
1624139Sjoerg *  See the file "Changes" for information on version-to-version changes.
1724139Sjoerg */
1824139Sjoerg
1924139Sjoerg/*
2024139Sjoerg *  This file contains "main" and other high-level routines.
2124139Sjoerg */
2224139Sjoerg
2324139Sjoerg/*
2424139Sjoerg * The following preprocessor variables, when defined, are used to
2524139Sjoerg * distinguish between different Unix implementations:
2624139Sjoerg *
2724139Sjoerg *	SIGHOLD  - use SVR4 sighold function when defined
2824139Sjoerg *	SIGRELSE - use SVR4 sigrelse function when defined
2924139Sjoerg *	FD_SET   - macros FD_SET and FD_ZERO are used when defined
3024139Sjoerg */
3124139Sjoerg
3224139Sjoerg#include "os.h"
3324139Sjoerg#include <signal.h>
3424139Sjoerg#include <setjmp.h>
3524139Sjoerg#include <ctype.h>
3624139Sjoerg#include <sys/time.h>
3724139Sjoerg
3824139Sjoerg/* includes specific to top */
3924139Sjoerg#include "display.h"		/* interface to display package */
4024139Sjoerg#include "screen.h"		/* interface to screen package */
4124139Sjoerg#include "top.h"
4224139Sjoerg#include "top.local.h"
4324139Sjoerg#include "boolean.h"
4424139Sjoerg#include "machine.h"
4524139Sjoerg#include "utils.h"
4624139Sjoerg
4724139Sjoerg/* Size of the stdio buffer given to stdout */
4824139Sjoerg#define Buffersize	2048
4924139Sjoerg
5024139Sjoerg/* The buffer that stdio will use */
5124139Sjoergchar stdoutbuf[Buffersize];
5224139Sjoerg
5324139Sjoerg/* build Signal masks */
5424139Sjoerg#define Smask(s)	(1 << ((s) - 1))
5524139Sjoerg
5624139Sjoerg/* for system errors */
5724139Sjoergextern int errno;
5824139Sjoerg
5924139Sjoerg/* for getopt: */
6024139Sjoergextern int  optind;
6124139Sjoergextern char *optarg;
6224139Sjoerg
6324139Sjoerg/* imported from screen.c */
6424139Sjoergextern int overstrike;
6524139Sjoerg
6624139Sjoerg/* signal handling routines */
6724139Sjoergsigret_t leave();
6824139Sjoergsigret_t onalrm();
6924139Sjoergsigret_t tstop();
7024139Sjoerg#ifdef SIGWINCH
7124139Sjoergsigret_t winch();
7224139Sjoerg#endif
7324139Sjoerg
7424139Sjoerg/* internal routines */
7524139Sjoergvoid quit();
7624139Sjoerg
7724139Sjoerg/* values which need to be accessed by signal handlers */
7824139Sjoergstatic int max_topn;		/* maximum displayable processes */
7924139Sjoerg
8024139Sjoerg/* miscellaneous things */
8124139Sjoergchar *myname = "top";
8224139Sjoergjmp_buf jmp_int;
8324139Sjoerg
8424139Sjoerg/* routines that don't return int */
8524139Sjoerg
8624139Sjoergchar *username();
8724139Sjoergchar *ctime();
8824139Sjoergchar *kill_procs();
8924139Sjoergchar *renice_procs();
9024139Sjoerg
9124139Sjoerg#ifdef ORDER
9224139Sjoergextern int (*proc_compares[])();
9324139Sjoerg#else
9424139Sjoergextern int proc_compare();
9524139Sjoerg#endif
9624139Sjoergtime_t time();
9724139Sjoerg
9824139Sjoergcaddr_t get_process_info();
9924139Sjoerg
10024139Sjoerg/* different routines for displaying the user's identification */
10124139Sjoerg/* (values assigned to get_userid) */
10224139Sjoergchar *username();
10324139Sjoergchar *itoa7();
10424139Sjoerg
10524139Sjoerg/* display routines that need to be predeclared */
10624139Sjoergint i_loadave();
10724139Sjoergint u_loadave();
10824139Sjoergint i_procstates();
10924139Sjoergint u_procstates();
11024139Sjoergint i_cpustates();
11124139Sjoergint u_cpustates();
11224139Sjoergint i_memory();
11324139Sjoergint u_memory();
11424142Sjoergint i_swap();
11524142Sjoergint u_swap();
11624139Sjoergint i_message();
11724139Sjoergint u_message();
11824139Sjoergint i_header();
11924139Sjoergint u_header();
12024139Sjoergint i_process();
12124139Sjoergint u_process();
12224139Sjoerg
12324139Sjoerg/* pointers to display routines */
12424139Sjoergint (*d_loadave)() = i_loadave;
12524139Sjoergint (*d_procstates)() = i_procstates;
12624139Sjoergint (*d_cpustates)() = i_cpustates;
12724139Sjoergint (*d_memory)() = i_memory;
12824142Sjoergint (*d_swap)() = i_swap;
12924139Sjoergint (*d_message)() = i_message;
13024139Sjoergint (*d_header)() = i_header;
13124139Sjoergint (*d_process)() = i_process;
13224139Sjoerg
13324139Sjoerg
13424139Sjoergmain(argc, argv)
13524139Sjoerg
13624139Sjoergint  argc;
13724139Sjoergchar *argv[];
13824139Sjoerg
13924139Sjoerg{
14024139Sjoerg    register int i;
14124139Sjoerg    register int active_procs;
14224139Sjoerg    register int change;
14324139Sjoerg
14424139Sjoerg    struct system_info system_info;
14524139Sjoerg    struct statics statics;
14624139Sjoerg    caddr_t processes;
14724139Sjoerg
14824139Sjoerg    static char tempbuf1[50];
14924139Sjoerg    static char tempbuf2[50];
15024139Sjoerg    int old_sigmask;		/* only used for BSD-style signals */
15124139Sjoerg    int topn = Default_TOPN;
15224139Sjoerg    int delay = Default_DELAY;
15324139Sjoerg    int displays = 0;		/* indicates unspecified */
15424139Sjoerg    time_t curr_time;
15524139Sjoerg    char *(*get_userid)() = username;
15624139Sjoerg    char *uname_field = "USERNAME";
15724139Sjoerg    char *header_text;
15824139Sjoerg    char *env_top;
15924139Sjoerg    char **preset_argv;
16024139Sjoerg    int  preset_argc = 0;
16124139Sjoerg    char **av;
16224139Sjoerg    int  ac;
16324139Sjoerg    char dostates = No;
16424139Sjoerg    char do_unames = Yes;
16524139Sjoerg    char interactive = Maybe;
16624139Sjoerg    char warnings = 0;
16724139Sjoerg#if Default_TOPN == Infinity
16824139Sjoerg    char topn_specified = No;
16924139Sjoerg#endif
17024139Sjoerg    char ch;
17124139Sjoerg    char *iptr;
17224139Sjoerg    char no_command = 1;
17324139Sjoerg    struct timeval timeout;
17424139Sjoerg    struct process_select ps;
17524139Sjoerg#ifdef ORDER
17624139Sjoerg    char *order_name = NULL;
17724139Sjoerg    int order_index = 0;
17824139Sjoerg#endif
17924139Sjoerg#ifndef FD_SET
18024139Sjoerg    /* FD_SET and friends are not present:  fake it */
18124139Sjoerg    typedef int fd_set;
18224139Sjoerg#define FD_ZERO(x)     (*(x) = 0)
18324139Sjoerg#define FD_SET(f, x)   (*(x) = f)
18424139Sjoerg#endif
18524139Sjoerg    fd_set readfds;
18624139Sjoerg
18724139Sjoerg#ifdef ORDER
18824139Sjoerg    static char command_chars[] = "\f qh?en#sdkriIuo";
18924139Sjoerg#else
19024139Sjoerg    static char command_chars[] = "\f qh?en#sdkriIu";
19124139Sjoerg#endif
19224139Sjoerg/* these defines enumerate the "strchr"s of the commands in command_chars */
19324139Sjoerg#define CMD_redraw	0
19424139Sjoerg#define CMD_update	1
19524139Sjoerg#define CMD_quit	2
19624139Sjoerg#define CMD_help1	3
19724139Sjoerg#define CMD_help2	4
19824139Sjoerg#define CMD_OSLIMIT	4    /* terminals with OS can only handle commands */
19924139Sjoerg#define CMD_errors	5    /* less than or equal to CMD_OSLIMIT	   */
20024139Sjoerg#define CMD_number1	6
20124139Sjoerg#define CMD_number2	7
20224139Sjoerg#define CMD_delay	8
20324139Sjoerg#define CMD_displays	9
20424139Sjoerg#define CMD_kill	10
20524139Sjoerg#define CMD_renice	11
20624139Sjoerg#define CMD_idletog     12
20724139Sjoerg#define CMD_idletog2    13
20824139Sjoerg#define CMD_user	14
20924139Sjoerg#ifdef ORDER
21024139Sjoerg#define CMD_order       15
21124139Sjoerg#endif
21224139Sjoerg
21324139Sjoerg    /* set the buffer for stdout */
21424139Sjoerg#ifdef DEBUG
21524139Sjoerg    setbuffer(stdout, NULL, 0);
21624139Sjoerg#else
21724139Sjoerg    setbuffer(stdout, stdoutbuf, Buffersize);
21824139Sjoerg#endif
21924139Sjoerg
22024139Sjoerg    /* get our name */
22124139Sjoerg    if (argc > 0)
22224139Sjoerg    {
22324139Sjoerg	if ((myname = strrchr(argv[0], '/')) == 0)
22424139Sjoerg	{
22524139Sjoerg	    myname = argv[0];
22624139Sjoerg	}
22724139Sjoerg	else
22824139Sjoerg	{
22924139Sjoerg	    myname++;
23024139Sjoerg	}
23124139Sjoerg    }
23224139Sjoerg
23324139Sjoerg    /* initialize some selection options */
23424139Sjoerg    ps.idle    = Yes;
23524139Sjoerg    ps.system  = No;
23624139Sjoerg    ps.uid     = -1;
23724139Sjoerg    ps.command = NULL;
23824139Sjoerg
23924139Sjoerg    /* get preset options from the environment */
24024139Sjoerg    if ((env_top = getenv("TOP")) != NULL)
24124139Sjoerg    {
24224139Sjoerg	av = preset_argv = argparse(env_top, &preset_argc);
24324139Sjoerg	ac = preset_argc;
24424139Sjoerg
24524139Sjoerg	/* set the dummy argument to an explanatory message, in case
24624139Sjoerg	   getopt encounters a bad argument */
24724139Sjoerg	preset_argv[0] = "while processing environment";
24824139Sjoerg    }
24924139Sjoerg
25024139Sjoerg    /* process options */
25124139Sjoerg    do {
25224139Sjoerg	/* if we're done doing the presets, then process the real arguments */
25324139Sjoerg	if (preset_argc == 0)
25424139Sjoerg	{
25524139Sjoerg	    ac = argc;
25624139Sjoerg	    av = argv;
25724139Sjoerg
25824139Sjoerg	    /* this should keep getopt happy... */
25924139Sjoerg	    optind = 1;
26024139Sjoerg	}
26124139Sjoerg
26224139Sjoerg	while ((i = getopt(ac, av, "SIbinqus:d:U:o:")) != EOF)
26324139Sjoerg	{
26424139Sjoerg	    switch(i)
26524139Sjoerg	    {
26624139Sjoerg	      case 'u':			/* toggle uid/username display */
26724139Sjoerg		do_unames = !do_unames;
26824139Sjoerg		break;
26924139Sjoerg
27024139Sjoerg	      case 'U':			/* display only username's processes */
27124139Sjoerg		if ((ps.uid = userid(optarg)) == -1)
27224139Sjoerg		{
27324139Sjoerg		    fprintf(stderr, "%s: unknown user\n", optarg);
27424139Sjoerg		    exit(1);
27524139Sjoerg		}
27624139Sjoerg		break;
27724139Sjoerg
27824139Sjoerg	      case 'S':			/* show system processes */
27924139Sjoerg		ps.system = !ps.system;
28024139Sjoerg		break;
28124139Sjoerg
28224139Sjoerg	      case 'I':                   /* show idle processes */
28324139Sjoerg		ps.idle = !ps.idle;
28424139Sjoerg		break;
28524139Sjoerg
28624139Sjoerg	      case 'i':			/* go interactive regardless */
28724139Sjoerg		interactive = Yes;
28824139Sjoerg		break;
28924139Sjoerg
29024139Sjoerg	      case 'n':			/* batch, or non-interactive */
29124139Sjoerg	      case 'b':
29224139Sjoerg		interactive = No;
29324139Sjoerg		break;
29424139Sjoerg
29524139Sjoerg	      case 'd':			/* number of displays to show */
29624139Sjoerg		if ((i = atoiwi(optarg)) == Invalid || i == 0)
29724139Sjoerg		{
29824139Sjoerg		    fprintf(stderr,
29924139Sjoerg			"%s: warning: display count should be positive -- option ignored\n",
30024139Sjoerg			myname);
30124139Sjoerg		    warnings++;
30224139Sjoerg		}
30324139Sjoerg		else
30424139Sjoerg		{
30524139Sjoerg		    displays = i;
30624139Sjoerg		}
30724139Sjoerg		break;
30824139Sjoerg
30924139Sjoerg	      case 's':
31024139Sjoerg		if ((delay = atoi(optarg)) < 0)
31124139Sjoerg		{
31224139Sjoerg		    fprintf(stderr,
31324139Sjoerg			"%s: warning: seconds delay should be non-negative -- using default\n",
31424139Sjoerg			myname);
31524139Sjoerg		    delay = Default_DELAY;
31624139Sjoerg		    warnings++;
31724139Sjoerg		}
31824139Sjoerg		break;
31924139Sjoerg
32024139Sjoerg	      case 'q':		/* be quick about it */
32124139Sjoerg		/* only allow this if user is really root */
32224139Sjoerg		if (getuid() == 0)
32324139Sjoerg		{
32424139Sjoerg		    /* be very un-nice! */
32524139Sjoerg		    (void) nice(-20);
32624139Sjoerg		}
32724139Sjoerg		else
32824139Sjoerg		{
32924139Sjoerg		    fprintf(stderr,
33024139Sjoerg			"%s: warning: `-q' option can only be used by root\n",
33124139Sjoerg			myname);
33224139Sjoerg		    warnings++;
33324139Sjoerg		}
33424139Sjoerg		break;
33524139Sjoerg
33624139Sjoerg	      case 'o':		/* select sort order */
33724139Sjoerg#ifdef ORDER
33824139Sjoerg		order_name = optarg;
33924139Sjoerg#else
34024139Sjoerg		fprintf(stderr,
34124139Sjoerg			"%s: this platform does not support arbitrary ordering.  Sorry.\n",
34224139Sjoerg			myname);
34324139Sjoerg		warnings++;
34424139Sjoerg#endif
34524139Sjoerg		break;
34624139Sjoerg
34724139Sjoerg	      default:
34824139Sjoerg		fprintf(stderr, "\
34924139SjoergTop version %s\n\
35024139SjoergUsage: %s [-ISbinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
35124139Sjoerg			version_string(), myname);
35224139Sjoerg		exit(1);
35324139Sjoerg	    }
35424139Sjoerg	}
35524139Sjoerg
35624139Sjoerg	/* get count of top processes to display (if any) */
35724139Sjoerg	if (optind < ac)
35824139Sjoerg	{
35924139Sjoerg	    if ((topn = atoiwi(av[optind])) == Invalid)
36024139Sjoerg	    {
36124139Sjoerg		fprintf(stderr,
36224139Sjoerg			"%s: warning: process display count should be non-negative -- using default\n",
36324139Sjoerg			myname);
36424139Sjoerg		warnings++;
36524139Sjoerg	    }
36624139Sjoerg#if Default_TOPN == Infinity
36724139Sjoerg            else
36824139Sjoerg	    {
36924139Sjoerg		topn_specified = Yes;
37024139Sjoerg	    }
37124139Sjoerg#endif
37224139Sjoerg	}
37324139Sjoerg
37424139Sjoerg	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
37524139Sjoerg	i = preset_argc;
37624139Sjoerg	preset_argc = 0;
37724139Sjoerg
37824139Sjoerg    /* repeat only if we really did the preset arguments */
37924139Sjoerg    } while (i != 0);
38024139Sjoerg
38124139Sjoerg    /* set constants for username/uid display correctly */
38224139Sjoerg    if (!do_unames)
38324139Sjoerg    {
38424139Sjoerg	uname_field = "   UID  ";
38524139Sjoerg	get_userid = itoa7;
38624139Sjoerg    }
38724139Sjoerg
38824139Sjoerg    /* initialize the kernel memory interface */
38924139Sjoerg    if (machine_init(&statics) == -1)
39024139Sjoerg    {
39124139Sjoerg	exit(1);
39224139Sjoerg    }
39324139Sjoerg
39424139Sjoerg#ifdef ORDER
39524139Sjoerg    /* determine sorting order index, if necessary */
39624139Sjoerg    if (order_name != NULL)
39724139Sjoerg    {
39824139Sjoerg	if ((order_index = string_index(order_name, statics.order_names)) == -1)
39924139Sjoerg	{
40024139Sjoerg	    char **pp;
40124139Sjoerg
40224139Sjoerg	    fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
40324139Sjoerg		    myname, order_name);
40424139Sjoerg	    fprintf(stderr, "\tTry one of these:");
40524139Sjoerg	    pp = statics.order_names;
40624139Sjoerg	    while (*pp != NULL)
40724139Sjoerg	    {
40824139Sjoerg		fprintf(stderr, " %s", *pp++);
40924139Sjoerg	    }
41024139Sjoerg	    fputc('\n', stderr);
41124139Sjoerg	    exit(1);
41224139Sjoerg	}
41324139Sjoerg    }
41424139Sjoerg#endif
41524139Sjoerg
41624139Sjoerg#ifdef no_initialization_needed
41724139Sjoerg    /* initialize the hashing stuff */
41824139Sjoerg    if (do_unames)
41924139Sjoerg    {
42024139Sjoerg	init_hash();
42124139Sjoerg    }
42224139Sjoerg#endif
42324139Sjoerg
42424139Sjoerg    /* initialize termcap */
42524139Sjoerg    init_termcap(interactive);
42624139Sjoerg
42724139Sjoerg    /* get the string to use for the process area header */
42824139Sjoerg    header_text = format_header(uname_field);
42924139Sjoerg
43024139Sjoerg    /* initialize display interface */
43124139Sjoerg    if ((max_topn = display_init(&statics)) == -1)
43224139Sjoerg    {
43324139Sjoerg	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
43424139Sjoerg	exit(4);
43524139Sjoerg    }
43624139Sjoerg
43724139Sjoerg    /* print warning if user requested more processes than we can display */
43824139Sjoerg    if (topn > max_topn)
43924139Sjoerg    {
44024139Sjoerg	fprintf(stderr,
44124139Sjoerg		"%s: warning: this terminal can only display %d processes.\n",
44224139Sjoerg		myname, max_topn);
44324139Sjoerg	warnings++;
44424139Sjoerg    }
44524139Sjoerg
44624139Sjoerg    /* adjust for topn == Infinity */
44724139Sjoerg    if (topn == Infinity)
44824139Sjoerg    {
44924139Sjoerg	/*
45024139Sjoerg	 *  For smart terminals, infinity really means everything that can
45124139Sjoerg	 *  be displayed, or Largest.
45224139Sjoerg	 *  On dumb terminals, infinity means every process in the system!
45324139Sjoerg	 *  We only really want to do that if it was explicitly specified.
45424139Sjoerg	 *  This is always the case when "Default_TOPN != Infinity".  But if
45524139Sjoerg	 *  topn wasn't explicitly specified and we are on a dumb terminal
45624139Sjoerg	 *  and the default is Infinity, then (and only then) we use
45724139Sjoerg	 *  "Nominal_TOPN" instead.
45824139Sjoerg	 */
45924139Sjoerg#if Default_TOPN == Infinity
46024139Sjoerg	topn = smart_terminal ? Largest :
46124139Sjoerg		    (topn_specified ? Largest : Nominal_TOPN);
46224139Sjoerg#else
46324139Sjoerg	topn = Largest;
46424139Sjoerg#endif
46524139Sjoerg    }
46624139Sjoerg
46724139Sjoerg    /* set header display accordingly */
46824139Sjoerg    display_header(topn > 0);
46924139Sjoerg
47024139Sjoerg    /* determine interactive state */
47124139Sjoerg    if (interactive == Maybe)
47224139Sjoerg    {
47324139Sjoerg	interactive = smart_terminal;
47424139Sjoerg    }
47524139Sjoerg
47624139Sjoerg    /* if # of displays not specified, fill it in */
47724139Sjoerg    if (displays == 0)
47824139Sjoerg    {
47924139Sjoerg	displays = smart_terminal ? Infinity : 1;
48024139Sjoerg    }
48124139Sjoerg
48224139Sjoerg    /* hold interrupt signals while setting up the screen and the handlers */
48324139Sjoerg#ifdef SIGHOLD
48424139Sjoerg    sighold(SIGINT);
48524139Sjoerg    sighold(SIGQUIT);
48624139Sjoerg    sighold(SIGTSTP);
48724139Sjoerg#else
48824139Sjoerg    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
48924139Sjoerg#endif
49024139Sjoerg    init_screen();
49124139Sjoerg    (void) signal(SIGINT, leave);
49224139Sjoerg    (void) signal(SIGQUIT, leave);
49324139Sjoerg    (void) signal(SIGTSTP, tstop);
49424139Sjoerg#ifdef SIGWINCH
49524139Sjoerg    (void) signal(SIGWINCH, winch);
49624139Sjoerg#endif
49724139Sjoerg#ifdef SIGRELSE
49824139Sjoerg    sigrelse(SIGINT);
49924139Sjoerg    sigrelse(SIGQUIT);
50024139Sjoerg    sigrelse(SIGTSTP);
50124139Sjoerg#else
50224139Sjoerg    (void) sigsetmask(old_sigmask);
50324139Sjoerg#endif
50424139Sjoerg    if (warnings)
50524139Sjoerg    {
50624139Sjoerg	fputs("....", stderr);
50724139Sjoerg	fflush(stderr);			/* why must I do this? */
50824139Sjoerg	sleep((unsigned)(3 * warnings));
50924139Sjoerg	fputc('\n', stderr);
51024139Sjoerg    }
51124139Sjoerg
51224139Sjoerg    /* setup the jump buffer for stops */
51324139Sjoerg    if (setjmp(jmp_int) != 0)
51424139Sjoerg    {
51524139Sjoerg	/* control ends up here after an interrupt */
51624139Sjoerg	reset_display();
51724139Sjoerg    }
51824139Sjoerg
51924139Sjoerg    /*
52024139Sjoerg     *  main loop -- repeat while display count is positive or while it
52124139Sjoerg     *		indicates infinity (by being -1)
52224139Sjoerg     */
52324139Sjoerg
52424139Sjoerg    while ((displays == -1) || (displays-- > 0))
52524139Sjoerg    {
52624139Sjoerg	/* get the current stats */
52724139Sjoerg	get_system_info(&system_info);
52824139Sjoerg
52924139Sjoerg	/* get the current set of processes */
53024139Sjoerg	processes =
53124139Sjoerg		get_process_info(&system_info,
53224139Sjoerg				 &ps,
53324139Sjoerg#ifdef ORDER
53424139Sjoerg				 proc_compares[order_index]);
53524139Sjoerg#else
53624139Sjoerg				 proc_compare);
53724139Sjoerg#endif
53824139Sjoerg
53924139Sjoerg	/* display the load averages */
54024139Sjoerg	(*d_loadave)(system_info.last_pid,
54124139Sjoerg		     system_info.load_avg);
54224139Sjoerg
54324139Sjoerg	/* display the current time */
54424139Sjoerg	/* this method of getting the time SHOULD be fairly portable */
54524139Sjoerg	time(&curr_time);
54624139Sjoerg	i_timeofday(&curr_time);
54724139Sjoerg
54824139Sjoerg	/* display process state breakdown */
54924139Sjoerg	(*d_procstates)(system_info.p_total,
55024139Sjoerg			system_info.procstates);
55124139Sjoerg
55224139Sjoerg	/* display the cpu state percentage breakdown */
55324139Sjoerg	if (dostates)	/* but not the first time */
55424139Sjoerg	{
55524139Sjoerg	    (*d_cpustates)(system_info.cpustates);
55624139Sjoerg	}
55724139Sjoerg	else
55824139Sjoerg	{
55924139Sjoerg	    /* we'll do it next time */
56024139Sjoerg	    if (smart_terminal)
56124139Sjoerg	    {
56224139Sjoerg		z_cpustates();
56324139Sjoerg	    }
56424139Sjoerg	    else
56524139Sjoerg	    {
56624139Sjoerg		putchar('\n');
56724139Sjoerg	    }
56824139Sjoerg	    dostates = Yes;
56924139Sjoerg	}
57024139Sjoerg
57124139Sjoerg	/* display memory stats */
57224139Sjoerg	(*d_memory)(system_info.memory);
57324139Sjoerg
57424142Sjoerg	/* display swap stats */
57524142Sjoerg	(*d_swap)(system_info.swap);
57624142Sjoerg
57724139Sjoerg	/* handle message area */
57824139Sjoerg	(*d_message)();
57924139Sjoerg
58024139Sjoerg	/* update the header area */
58124139Sjoerg	(*d_header)(header_text);
58224139Sjoerg
58324139Sjoerg	if (topn > 0)
58424139Sjoerg	{
58524139Sjoerg	    /* determine number of processes to actually display */
58624139Sjoerg	    /* this number will be the smallest of:  active processes,
58724139Sjoerg	       number user requested, number current screen accomodates */
58824139Sjoerg	    active_procs = system_info.p_active;
58924139Sjoerg	    if (active_procs > topn)
59024139Sjoerg	    {
59124139Sjoerg		active_procs = topn;
59224139Sjoerg	    }
59324139Sjoerg	    if (active_procs > max_topn)
59424139Sjoerg	    {
59524139Sjoerg		active_procs = max_topn;
59624139Sjoerg	    }
59724139Sjoerg
59824139Sjoerg	    /* now show the top "n" processes. */
59924139Sjoerg	    for (i = 0; i < active_procs; i++)
60024139Sjoerg	    {
60124139Sjoerg		(*d_process)(i, format_next_process(processes, get_userid));
60224139Sjoerg	    }
60324139Sjoerg	}
60424139Sjoerg	else
60524139Sjoerg	{
60624139Sjoerg	    i = 0;
60724139Sjoerg	}
60824139Sjoerg
60924139Sjoerg	/* do end-screen processing */
61024139Sjoerg	u_endscreen(i);
61124139Sjoerg
61224139Sjoerg	/* now, flush the output buffer */
61324139Sjoerg	fflush(stdout);
61424139Sjoerg
61524139Sjoerg	/* only do the rest if we have more displays to show */
61624139Sjoerg	if (displays)
61724139Sjoerg	{
61824139Sjoerg	    /* switch out for new display on smart terminals */
61924139Sjoerg	    if (smart_terminal)
62024139Sjoerg	    {
62124139Sjoerg		if (overstrike)
62224139Sjoerg		{
62324139Sjoerg		    reset_display();
62424139Sjoerg		}
62524139Sjoerg		else
62624139Sjoerg		{
62724139Sjoerg		    d_loadave = u_loadave;
62824139Sjoerg		    d_procstates = u_procstates;
62924139Sjoerg		    d_cpustates = u_cpustates;
63024139Sjoerg		    d_memory = u_memory;
63124142Sjoerg		    d_swap = u_swap;
63224139Sjoerg		    d_message = u_message;
63324139Sjoerg		    d_header = u_header;
63424139Sjoerg		    d_process = u_process;
63524139Sjoerg		}
63624139Sjoerg	    }
63724139Sjoerg
63824139Sjoerg	    no_command = Yes;
63924139Sjoerg	    if (!interactive)
64024139Sjoerg	    {
64124139Sjoerg		/* set up alarm */
64224139Sjoerg		(void) signal(SIGALRM, onalrm);
64324139Sjoerg		(void) alarm((unsigned)delay);
64424139Sjoerg
64524139Sjoerg		/* wait for the rest of it .... */
64624139Sjoerg		pause();
64724139Sjoerg	    }
64824139Sjoerg	    else while (no_command)
64924139Sjoerg	    {
65024139Sjoerg		/* assume valid command unless told otherwise */
65124139Sjoerg		no_command = No;
65224139Sjoerg
65324139Sjoerg		/* set up arguments for select with timeout */
65424139Sjoerg		FD_ZERO(&readfds);
65524139Sjoerg		FD_SET(1, &readfds);		/* for standard input */
65624139Sjoerg		timeout.tv_sec  = delay;
65724139Sjoerg		timeout.tv_usec = 0;
65824139Sjoerg
65924139Sjoerg		/* wait for either input or the end of the delay period */
66024139Sjoerg		if (select(32, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) > 0)
66124139Sjoerg		{
66224139Sjoerg		    int newval;
66324139Sjoerg		    char *errmsg;
66424139Sjoerg
66524139Sjoerg		    /* something to read -- clear the message area first */
66624139Sjoerg		    clear_message();
66724139Sjoerg
66824139Sjoerg		    /* now read it and convert to command strchr */
66924139Sjoerg		    /* (use "change" as a temporary to hold strchr) */
67024139Sjoerg		    (void) read(0, &ch, 1);
67124139Sjoerg		    if ((iptr = strchr(command_chars, ch)) == NULL)
67224139Sjoerg		    {
67324142Sjoerg			if (ch != '\r' && ch != '\n')
67424142Sjoerg			{
67524142Sjoerg			    /* illegal command */
67624142Sjoerg			    new_message(MT_standout, " Command not understood");
67724142Sjoerg			}
67824139Sjoerg			putchar('\r');
67924139Sjoerg			no_command = Yes;
68024139Sjoerg		    }
68124139Sjoerg		    else
68224139Sjoerg		    {
68324139Sjoerg			change = iptr - command_chars;
68424139Sjoerg			if (overstrike && change > CMD_OSLIMIT)
68524139Sjoerg			{
68624139Sjoerg			    /* error */
68724139Sjoerg			    new_message(MT_standout,
68824139Sjoerg			    " Command cannot be handled by this terminal");
68924139Sjoerg			    putchar('\r');
69024139Sjoerg			    no_command = Yes;
69124139Sjoerg			}
69224139Sjoerg			else switch(change)
69324139Sjoerg			{
69424139Sjoerg			    case CMD_redraw:	/* redraw screen */
69524139Sjoerg				reset_display();
69624139Sjoerg				break;
69724139Sjoerg
69824139Sjoerg			    case CMD_update:	/* merely update display */
69924139Sjoerg				/* is the load average high? */
70024139Sjoerg				if (system_info.load_avg[0] > LoadMax)
70124139Sjoerg				{
70224139Sjoerg				    /* yes, go home for visual feedback */
70324139Sjoerg				    go_home();
70424139Sjoerg				    fflush(stdout);
70524139Sjoerg				}
70624139Sjoerg				break;
70724139Sjoerg
70824139Sjoerg			    case CMD_quit:	/* quit */
70924139Sjoerg				quit(0);
71024139Sjoerg				/*NOTREACHED*/
71124139Sjoerg				break;
71224139Sjoerg
71324139Sjoerg			    case CMD_help1:	/* help */
71424139Sjoerg			    case CMD_help2:
71524139Sjoerg				reset_display();
71624139Sjoerg				clear();
71724139Sjoerg				show_help();
71824139Sjoerg				standout("Hit any key to continue: ");
71924139Sjoerg				fflush(stdout);
72024139Sjoerg				(void) read(0, &ch, 1);
72124139Sjoerg				break;
72224139Sjoerg
72324139Sjoerg			    case CMD_errors:	/* show errors */
72424139Sjoerg				if (error_count() == 0)
72524139Sjoerg				{
72624139Sjoerg				    new_message(MT_standout,
72724139Sjoerg					" Currently no errors to report.");
72824139Sjoerg				    putchar('\r');
72924139Sjoerg				    no_command = Yes;
73024139Sjoerg				}
73124139Sjoerg				else
73224139Sjoerg				{
73324139Sjoerg				    reset_display();
73424139Sjoerg				    clear();
73524139Sjoerg				    show_errors();
73624139Sjoerg				    standout("Hit any key to continue: ");
73724139Sjoerg				    fflush(stdout);
73824139Sjoerg				    (void) read(0, &ch, 1);
73924139Sjoerg				}
74024139Sjoerg				break;
74124139Sjoerg
74224139Sjoerg			    case CMD_number1:	/* new number */
74324139Sjoerg			    case CMD_number2:
74424139Sjoerg				new_message(MT_standout,
74524139Sjoerg				    "Number of processes to show: ");
74624139Sjoerg				newval = readline(tempbuf1, 8, Yes);
74724139Sjoerg				if (newval > -1)
74824139Sjoerg				{
74924139Sjoerg				    if (newval > max_topn)
75024139Sjoerg				    {
75124139Sjoerg					new_message(MT_standout | MT_delayed,
75224139Sjoerg					  " This terminal can only display %d processes.",
75324139Sjoerg					  max_topn);
75424139Sjoerg					putchar('\r');
75524139Sjoerg				    }
75624139Sjoerg
75724139Sjoerg				    if (newval == 0)
75824139Sjoerg				    {
75924139Sjoerg					/* inhibit the header */
76024139Sjoerg					display_header(No);
76124139Sjoerg				    }
76224139Sjoerg				    else if (newval > topn && topn == 0)
76324139Sjoerg				    {
76424139Sjoerg					/* redraw the header */
76524139Sjoerg					display_header(Yes);
76624139Sjoerg					d_header = i_header;
76724139Sjoerg				    }
76824139Sjoerg				    topn = newval;
76924139Sjoerg				}
77024139Sjoerg				break;
77124139Sjoerg
77224139Sjoerg			    case CMD_delay:	/* new seconds delay */
77324139Sjoerg				new_message(MT_standout, "Seconds to delay: ");
77424139Sjoerg				if ((i = readline(tempbuf1, 8, Yes)) > -1)
77524139Sjoerg				{
77624139Sjoerg				    delay = i;
77724139Sjoerg				}
77824139Sjoerg				clear_message();
77924139Sjoerg				break;
78024139Sjoerg
78124139Sjoerg			    case CMD_displays:	/* change display count */
78224139Sjoerg				new_message(MT_standout,
78324139Sjoerg					"Displays to show (currently %s): ",
78424139Sjoerg					displays == -1 ? "infinite" :
78524139Sjoerg							 itoa(displays));
78624139Sjoerg				if ((i = readline(tempbuf1, 10, Yes)) > 0)
78724139Sjoerg				{
78824139Sjoerg				    displays = i;
78924139Sjoerg				}
79024139Sjoerg				else if (i == 0)
79124139Sjoerg				{
79224139Sjoerg				    quit(0);
79324139Sjoerg				}
79424139Sjoerg				clear_message();
79524139Sjoerg				break;
79624139Sjoerg
79724139Sjoerg			    case CMD_kill:	/* kill program */
79824139Sjoerg				new_message(0, "kill ");
79924139Sjoerg				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
80024139Sjoerg				{
80124139Sjoerg				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
80224139Sjoerg				    {
80324139Sjoerg					new_message(MT_standout, errmsg);
80424139Sjoerg					putchar('\r');
80524139Sjoerg					no_command = Yes;
80624139Sjoerg				    }
80724139Sjoerg				}
80824139Sjoerg				else
80924139Sjoerg				{
81024139Sjoerg				    clear_message();
81124139Sjoerg				}
81224139Sjoerg				break;
81324139Sjoerg
81424139Sjoerg			    case CMD_renice:	/* renice program */
81524139Sjoerg				new_message(0, "renice ");
81624139Sjoerg				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
81724139Sjoerg				{
81824139Sjoerg				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
81924139Sjoerg				    {
82024139Sjoerg					new_message(MT_standout, errmsg);
82124139Sjoerg					putchar('\r');
82224139Sjoerg					no_command = Yes;
82324139Sjoerg				    }
82424139Sjoerg				}
82524139Sjoerg				else
82624139Sjoerg				{
82724139Sjoerg				    clear_message();
82824139Sjoerg				}
82924139Sjoerg				break;
83024139Sjoerg
83124139Sjoerg			    case CMD_idletog:
83224139Sjoerg			    case CMD_idletog2:
83324139Sjoerg				ps.idle = !ps.idle;
83424139Sjoerg				new_message(MT_standout | MT_delayed,
83524139Sjoerg				    " %sisplaying idle processes.",
83624139Sjoerg				    ps.idle ? "D" : "Not d");
83724139Sjoerg				putchar('\r');
83824139Sjoerg				break;
83924139Sjoerg
84024139Sjoerg			    case CMD_user:
84124139Sjoerg				new_message(MT_standout,
84224139Sjoerg				    "Username to show: ");
84324139Sjoerg				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
84424139Sjoerg				{
84524139Sjoerg				    if (tempbuf2[0] == '+' &&
84624139Sjoerg					tempbuf2[1] == '\0')
84724139Sjoerg				    {
84824139Sjoerg					ps.uid = -1;
84924139Sjoerg				    }
85024139Sjoerg				    else if ((i = userid(tempbuf2)) == -1)
85124139Sjoerg				    {
85224139Sjoerg					new_message(MT_standout,
85324139Sjoerg					    " %s: unknown user", tempbuf2);
85424139Sjoerg					no_command = Yes;
85524139Sjoerg				    }
85624139Sjoerg				    else
85724139Sjoerg				    {
85824139Sjoerg					ps.uid = i;
85924139Sjoerg				    }
86024139Sjoerg				    putchar('\r');
86124139Sjoerg				}
86224139Sjoerg				else
86324139Sjoerg				{
86424139Sjoerg				    clear_message();
86524139Sjoerg				}
86624139Sjoerg				break;
86724139Sjoerg
86824139Sjoerg#ifdef ORDER
86924139Sjoerg			    case CMD_order:
87024139Sjoerg				new_message(MT_standout,
87124139Sjoerg				    "Order to sort: ");
87224139Sjoerg				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
87324139Sjoerg				{
87424139Sjoerg				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
87524139Sjoerg					{
87624139Sjoerg					  new_message(MT_standout,
87724139Sjoerg					      " %s: unrecognized sorting order", tempbuf2);
87824139Sjoerg					  no_command = Yes;
87924139Sjoerg				    }
88024139Sjoerg				    else
88124139Sjoerg				    {
88224139Sjoerg					order_index = i;
88324139Sjoerg				    }
88424139Sjoerg				    putchar('\r');
88524139Sjoerg				}
88624139Sjoerg				else
88724139Sjoerg				{
88824139Sjoerg				    clear_message();
88924139Sjoerg				}
89024139Sjoerg				break;
89124139Sjoerg#endif
89224139Sjoerg
89324139Sjoerg			    default:
89424139Sjoerg				new_message(MT_standout, " BAD CASE IN SWITCH!");
89524139Sjoerg				putchar('\r');
89624139Sjoerg			}
89724139Sjoerg		    }
89824139Sjoerg
89924139Sjoerg		    /* flush out stuff that may have been written */
90024139Sjoerg		    fflush(stdout);
90124139Sjoerg		}
90224139Sjoerg	    }
90324139Sjoerg	}
90424139Sjoerg    }
90524139Sjoerg
90624139Sjoerg    quit(0);
90724139Sjoerg    /*NOTREACHED*/
90824139Sjoerg}
90924139Sjoerg
91024139Sjoerg/*
91124139Sjoerg *  reset_display() - reset all the display routine pointers so that entire
91224139Sjoerg *	screen will get redrawn.
91324139Sjoerg */
91424139Sjoerg
91524139Sjoergreset_display()
91624139Sjoerg
91724139Sjoerg{
91824139Sjoerg    d_loadave    = i_loadave;
91924139Sjoerg    d_procstates = i_procstates;
92024139Sjoerg    d_cpustates  = i_cpustates;
92124139Sjoerg    d_memory     = i_memory;
92224142Sjoerg    d_swap       = i_swap;
92324139Sjoerg    d_message	 = i_message;
92424139Sjoerg    d_header	 = i_header;
92524139Sjoerg    d_process	 = i_process;
92624139Sjoerg}
92724139Sjoerg
92824139Sjoerg/*
92924139Sjoerg *  signal handlers
93024139Sjoerg */
93124139Sjoerg
93224139Sjoergsigret_t leave()	/* exit under normal conditions -- INT handler */
93324139Sjoerg
93424139Sjoerg{
93524139Sjoerg    end_screen();
93624139Sjoerg    exit(0);
93724139Sjoerg}
93824139Sjoerg
93924139Sjoergsigret_t tstop(i)	/* SIGTSTP handler */
94024139Sjoerg
94124139Sjoergint i;
94224139Sjoerg
94324139Sjoerg{
94424139Sjoerg    /* move to the lower left */
94524139Sjoerg    end_screen();
94624139Sjoerg    fflush(stdout);
94724139Sjoerg
94824139Sjoerg    /* default the signal handler action */
94924139Sjoerg    (void) signal(SIGTSTP, SIG_DFL);
95024139Sjoerg
95124139Sjoerg    /* unblock the signal and send ourselves one */
95224139Sjoerg#ifdef SIGRELSE
95324139Sjoerg    sigrelse(SIGTSTP);
95424139Sjoerg#else
95524139Sjoerg    (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
95624139Sjoerg#endif
95724139Sjoerg    (void) kill(0, SIGTSTP);
95824139Sjoerg
95924139Sjoerg    /* reset the signal handler */
96024139Sjoerg    (void) signal(SIGTSTP, tstop);
96124139Sjoerg
96224139Sjoerg    /* reinit screen */
96324139Sjoerg    reinit_screen();
96424139Sjoerg
96524139Sjoerg    /* jump to appropriate place */
96624139Sjoerg    longjmp(jmp_int, 1);
96724139Sjoerg
96824139Sjoerg    /*NOTREACHED*/
96924139Sjoerg}
97024139Sjoerg
97124139Sjoerg#ifdef SIGWINCH
97224139Sjoergsigret_t winch(i)		/* SIGWINCH handler */
97324139Sjoerg
97424139Sjoergint i;
97524139Sjoerg
97624139Sjoerg{
97724139Sjoerg    /* reascertain the screen dimensions */
97824139Sjoerg    get_screensize();
97924139Sjoerg
98024139Sjoerg    /* tell display to resize */
98124139Sjoerg    max_topn = display_resize();
98224139Sjoerg
98324139Sjoerg    /* reset the signal handler */
98424139Sjoerg    (void) signal(SIGWINCH, winch);
98524139Sjoerg
98624139Sjoerg    /* jump to appropriate place */
98724139Sjoerg    longjmp(jmp_int, 1);
98824139Sjoerg}
98924139Sjoerg#endif
99024139Sjoerg
99124139Sjoergvoid quit(status)		/* exit under duress */
99224139Sjoerg
99324139Sjoergint status;
99424139Sjoerg
99524139Sjoerg{
99624139Sjoerg    end_screen();
99724139Sjoerg    exit(status);
99824139Sjoerg    /*NOTREACHED*/
99924139Sjoerg}
100024139Sjoerg
100124139Sjoergsigret_t onalrm()	/* SIGALRM handler */
100224139Sjoerg
100324139Sjoerg{
100424139Sjoerg    /* this is only used in batch mode to break out of the pause() */
100524139Sjoerg    /* return; */
100624139Sjoerg}
100724139Sjoerg
1008