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