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