top.c revision 322509
1139823Simpchar *copyright =
221830Sjoerg    "Copyright (c) 1984 through 1996, William LeFebvre";
321830Sjoerg
47055Sdg/*
57055Sdg *  Top users/processes display for Unix
67055Sdg *  Version 3
77055Sdg *
87055Sdg *  This program may be freely redistributed,
97055Sdg *  but this entire comment MUST remain intact.
107055Sdg *
117055Sdg *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
127055Sdg *  Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
137055Sdg *  Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
147055Sdg *  Copyright (c) 1996, William LeFebvre, Group sys Consulting
157055Sdg *
167055Sdg * $FreeBSD: stable/11/contrib/top/top.c 322509 2017-08-14 15:08:49Z gahr $
177055Sdg */
187055Sdg
197055Sdg/*
207055Sdg *  See the file "Changes" for information on version-to-version changes.
217055Sdg */
227055Sdg
237055Sdg/*
247055Sdg *  This file contains "main" and other high-level routines.
257055Sdg */
267055Sdg
277055Sdg/*
287055Sdg * The following preprocessor variables, when defined, are used to
297055Sdg * distinguish between different Unix implementations:
307055Sdg *
317055Sdg *	SIGHOLD  - use SVR4 sighold function when defined
327055Sdg *	SIGRELSE - use SVR4 sigrelse function when defined
337055Sdg *	FD_SET   - macros FD_SET and FD_ZERO are used when defined
347055Sdg */
357061Sdg
3650477Speter#include "os.h"
377055Sdg
387055Sdg#include <sys/jail.h>
3932356Seivind#include <sys/time.h>
4032350Seivind
4154263Sshin#include <ctype.h>
4231742Seivind#include <errno.h>
4331742Seivind#include <jail.h>
447055Sdg#include <setjmp.h>
457055Sdg#include <signal.h>
4693375Smdodd#include <unistd.h>
4793375Smdodd
487055Sdg/* includes specific to top */
4993375Smdodd#include "commands.h"
507055Sdg#include "display.h"		/* interface to display package */
5193375Smdodd#include "screen.h"		/* interface to screen package */
527055Sdg#include "top.h"
537055Sdg#include "top.local.h"
54112271Smdodd#include "boolean.h"
557055Sdg#include "machine.h"
567055Sdg#include "utils.h"
57186119Sqingli#include "username.h"
58112271Smdodd
59184709Sbz/* Size of the stdio buffer given to stdout */
6093375Smdodd#define Buffersize	2048
6193375Smdodd
6293375Smdodd/* The buffer that stdio will use */
6393373Smdoddchar stdoutbuf[Buffersize];
647055Sdg
6554263Sshin/* build Signal masks */
667055Sdg#define Smask(s)	(1 << ((s) - 1))
677055Sdg
6832350Seivind/* for getopt: */
697055Sdgextern int  optind;
7054263Sshinextern char *optarg;
7154263Sshin
7254263Sshin/* imported from screen.c */
737055Sdgextern int overstrike;
7411819Sjulian
7521830Sjoergstatic int fmt_flags = 0;
7611819Sjulianint pcpu_stats = No;
7711819Sjulian
7811819Sjulian/* signal handling routines */
797055Sdgsigret_t leave();
807055Sdgsigret_t tstop();
817055Sdg#ifdef SIGWINCH
827055Sdgsigret_t winch();
8321830Sjoerg#endif
8421830Sjoerg
8521830Sjoergvolatile sig_atomic_t leaveflag;
8621830Sjoergvolatile sig_atomic_t tstopflag;
8721830Sjoergvolatile sig_atomic_t winchflag;
8821830Sjoerg
8921830Sjoerg/* internal routines */
9021830Sjoergvoid quit();
9121830Sjoerg
92163606Srwatson/* values which need to be accessed by signal handlers */
93163606Srwatsonstatic int max_topn;		/* maximum displayable processes */
94126788Srwatson
9593382Smdodd/* miscellaneous things */
9693382Smdoddstruct process_select ps;
9793383Smdoddchar *myname = "top";
9893084Sbdejmp_buf jmp_int;
99249925Sglebius
100191148Skmacy/* routines that don't return int */
101106939Ssam
10268180Sumechar *username();
103112276Smdoddchar *ctime();
10493369Smdoddchar *kill_procs();
1057055Sdgchar *renice_procs();
1067055Sdg
1077055Sdg#ifdef ORDER
1087055Sdgextern int (*compares[])();
1097055Sdg#else
1107055Sdgextern int proc_compare();
1117055Sdgextern int io_compare();
11293383Smdodd#endif
113249925Sglebiustime_t time();
114249925Sglebius
1157055Sdgcaddr_t get_process_info();
11621830Sjoerg
11769152Sjlemon/* different routines for displaying the user's identification */
11893373Smdodd/* (values assigned to get_userid) */
11993367Smdoddchar *username();
120193891Sbzchar *itoa7();
121186119Sqingli
122193891Sbz/* pointers to display routines */
1237055Sdgvoid (*d_loadave)() = i_loadave;
124105577Srwatsonvoid (*d_procstates)() = i_procstates;
125172930Srwatsonvoid (*d_cpustates)() = i_cpustates;
126105577Srwatsonvoid (*d_memory)() = i_memory;
127105577Srwatsonvoid (*d_arc)() = i_arc;
128105577Srwatsonvoid (*d_carc)() = i_carc;
129105577Srwatsonvoid (*d_swap)() = i_swap;
130112308Smdoddvoid (*d_message)() = i_message;
131112308Smdoddvoid (*d_header)() = i_header;
132148887Srwatsonvoid (*d_process)() = i_process;
133148887Srwatson
1347055Sdgvoid reset_display(void);
13534961Sphk
136111767Smdoddstatic void
1377055Sdgreset_uids()
1387055Sdg{
13921830Sjoerg    for (size_t i = 0; i < TOP_MAX_UIDS; ++i)
140193891Sbz	ps.uid[i] = -1;
141193891Sbz}
142193891Sbz
143193891Sbzstatic int
144186119Sqingliadd_uid(int uid)
145128636Sluigi{
146128636Sluigi    size_t i = 0;
14721830Sjoerg
1487055Sdg    /* Add the uid if there's room */
14921830Sjoerg    for (; i < TOP_MAX_UIDS; ++i)
150126951Smdodd    {
151126951Smdodd	if (ps.uid[i] == -1 || ps.uid[i] == uid)
152126951Smdodd	{
153126951Smdodd	    ps.uid[i] = uid;
154126951Smdodd	    break;
155126951Smdodd	}
156126951Smdodd    }
157126951Smdodd
158126951Smdodd    return (i == TOP_MAX_UIDS);
159126951Smdodd}
160126951Smdodd
161126951Smdoddstatic void
162126951Smdoddrem_uid(int uid)
163126951Smdodd{
164126951Smdodd    size_t i = 0;
165126951Smdodd    size_t where = TOP_MAX_UIDS;
166126951Smdodd
167126951Smdodd    /* Look for the user to remove - no problem if it's not there */
168126951Smdodd    for (; i < TOP_MAX_UIDS; ++i)
169126951Smdodd    {
170126951Smdodd	if (ps.uid[i] == -1)
171126951Smdodd	    break;
172126951Smdodd	if (ps.uid[i] == uid)
173126951Smdodd	    where = i;
174126951Smdodd    }
175126951Smdodd
176126951Smdodd    /* Make sure we don't leave a hole in the middle */
177112266Smdodd    if (where != TOP_MAX_UIDS)
17854263Sshin    {
17954263Sshin	ps.uid[where] = ps.uid[i-1];
180186217Sqingli	ps.uid[i-1] = -1;
181128636Sluigi    }
182128636Sluigi}
18354263Sshin
18454263Sshinstatic int
185112266Smdoddhandle_user(char *buf, size_t buflen)
18611819Sjulian{
18711819Sjulian    int rc = 0;
18821830Sjoerg    int uid = -1;
189249925Sglebius    char *buf2 = buf;
190249925Sglebius
19111819Sjulian    new_message(MT_standout, "Username to show (+ for all): ");
192112266Smdodd    if (readline(buf, buflen, No) <= 0)
19321830Sjoerg    {
19421830Sjoerg	clear_message();
19521830Sjoerg	return rc;
196249925Sglebius    }
19721830Sjoerg
19821830Sjoerg    if (buf[0] == '+' || buf[0] == '-')
19921830Sjoerg    {
20021830Sjoerg	if (buf[1] == '\0')
201249925Sglebius	{
20221830Sjoerg	    reset_uids();
20321830Sjoerg	    goto end;
20421830Sjoerg	}
20521830Sjoerg	else
20621830Sjoerg	    ++buf2;
20721830Sjoerg    }
20821830Sjoerg
20921830Sjoerg    if ((uid = userid(buf2)) == -1)
21021830Sjoerg    {
21121830Sjoerg	new_message(MT_standout, " %s: unknown user", buf2);
212243882Sglebius	rc = 1;
21321830Sjoerg	goto end;
21421830Sjoerg    }
21593371Smdodd
21693371Smdodd    if (buf2 == buf)
21793373Smdodd    {
21821830Sjoerg	reset_uids();
21921830Sjoerg	ps.uid[0] = uid;
22021830Sjoerg	goto end;
22121830Sjoerg    }
222194819Srwatson
22321830Sjoerg    if (buf[0] == '+')
22421830Sjoerg    {
22521830Sjoerg	if (add_uid(uid))
2267055Sdg	{
22752248Smsmith	    new_message(MT_standout, " too many users, reset with '+'");
22852248Smsmith	    rc = 1;
229249925Sglebius	    goto end;
230249925Sglebius	}
23152248Smsmith    }
232249925Sglebius    else
233249925Sglebius	rem_uid(uid);
23452248Smsmith
23552248Smsmithend:
23652248Smsmith    putchar('\r');
2377055Sdg    return rc;
2387055Sdg}
239249925Sglebius
240249925Sglebiusint
24136992Sjulianmain(argc, argv)
242249925Sglebius
243249925Sglebiusint  argc;
2447055Sdgchar *argv[];
2457055Sdg
2467055Sdg{
2477055Sdg    register int i;
2487055Sdg    register int active_procs;
2497055Sdg    register int change;
2507055Sdg
2517055Sdg    struct system_info system_info;
2527055Sdg    struct statics statics;
2537055Sdg    caddr_t processes;
2547055Sdg
2557055Sdg    static char tempbuf1[50];
2567055Sdg    static char tempbuf2[50];
2577055Sdg    int old_sigmask;		/* only used for BSD-style signals */
2587055Sdg    int topn = Default_TOPN;
2597055Sdg    int delay = Default_DELAY;
2607055Sdg    int displays = 0;		/* indicates unspecified */
2617055Sdg    int sel_ret = 0;
2627055Sdg    time_t curr_time;
2637055Sdg    char *(*get_userid)() = username;
2647055Sdg    char *uname_field = "USERNAME";
2657055Sdg    char *header_text;
2667055Sdg    char *env_top;
2677055Sdg    char **preset_argv;
2687055Sdg    int  preset_argc = 0;
2697055Sdg    char **av;
2707055Sdg    int  ac;
2717055Sdg    char dostates = No;
2727055Sdg    char do_unames = Yes;
2737055Sdg    char interactive = Maybe;
2747055Sdg    char warnings = 0;
2757055Sdg#if Default_TOPN == Infinity
2767055Sdg    char topn_specified = No;
2777055Sdg#endif
2787055Sdg    char ch;
2797055Sdg    char *iptr;
2807055Sdg    char no_command = 1;
2817055Sdg    struct timeval timeout;
2827055Sdg#ifdef ORDER
2837055Sdg    char *order_name = NULL;
284105598Sbrooks    int order_index = 0;
2857055Sdg#endif
2867055Sdg#ifndef FD_SET
2877055Sdg    /* FD_SET and friends are not present:  fake it */
28893380Smdodd    typedef int fd_set;
28993380Smdodd#define FD_ZERO(x)     (*(x) = 0)
29093380Smdodd#define FD_SET(f, x)   (*(x) = 1<<f)
2917055Sdg#endif
29293367Smdodd    fd_set readfds;
293243882Sglebius
2947055Sdg#ifdef ORDER
2957055Sdg    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJwo";
2967055Sdg#else
2977055Sdg    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJw";
2987055Sdg#endif
299112266Smdodd/* these defines enumerate the "strchr"s of the commands in command_chars */
300112266Smdodd#define CMD_redraw	0
301112266Smdodd#define CMD_update	1
302112266Smdodd#define CMD_quit	2
3037055Sdg#define CMD_help1	3
30436908Sjulian#define CMD_help2	4
3057055Sdg#define CMD_OSLIMIT	4    /* terminals with OS can only handle commands */
3067055Sdg#define CMD_errors	5    /* less than or equal to CMD_OSLIMIT	   */
3077055Sdg#define CMD_number1	6
3087055Sdg#define CMD_number2	7
309243882Sglebius#define CMD_delay	8
3107055Sdg#define CMD_displays	9
3117055Sdg#define CMD_kill	10
3127055Sdg#define CMD_renice	11
3137055Sdg#define CMD_idletog     12
31493375Smdodd#define CMD_idletog2    13
3157055Sdg#define CMD_user	14
31652248Smsmith#define CMD_selftog	15
31793375Smdodd#define CMD_thrtog	16
31852248Smsmith#define CMD_viewtog	17
319152315Sru#define CMD_viewsys	18
32093373Smdodd#define	CMD_wcputog	19
32193377Smdodd#define	CMD_showargs	20
32236908Sjulian#define	CMD_jidtog	21
32336908Sjulian#define CMD_kidletog	22
32436908Sjulian#define CMD_pcputog	23
32536908Sjulian#define CMD_jail	24
32636908Sjulian#define CMD_swaptog	25
32736908Sjulian#ifdef ORDER
32836908Sjulian#define CMD_order       26
32936908Sjulian#endif
33036908Sjulian
33193377Smdodd    /* set the buffer for stdout */
332112279Smdodd#ifdef DEBUG
333112279Smdodd    extern FILE *debug;
334112279Smdodd    debug = fopen("debug.run", "w");
335112279Smdodd    setbuffer(stdout, NULL, 0);
336112279Smdodd#else
337112279Smdodd    setbuffer(stdout, stdoutbuf, Buffersize);
338112279Smdodd#endif
339112279Smdodd
340112279Smdodd    /* get our name */
34193369Smdodd    if (argc > 0)
34236908Sjulian    {
34336908Sjulian	if ((myname = strrchr(argv[0], '/')) == 0)
34436908Sjulian	{
345185164Skmacy	    myname = argv[0];
346130549Smlaier	}
347130549Smlaier	else
348130549Smlaier	{
3497055Sdg	    myname++;
3507055Sdg	}
3517055Sdg    }
35293379Smdodd
3537055Sdg    /* initialize some selection options */
3547055Sdg    ps.idle    = Yes;
3557055Sdg    ps.self    = -1;
3567055Sdg    ps.system  = No;
3577055Sdg    reset_uids();
3587055Sdg    ps.thread  = No;
359112308Smdodd    ps.wcpu    = 1;
3607055Sdg    ps.jid     = -1;
361106939Ssam    ps.jail    = No;
362106939Ssam    ps.swap    = No;
3637055Sdg    ps.kidle   = Yes;
3647055Sdg    ps.command = NULL;
3657055Sdg
366111888Sjlemon    /* get preset options from the environment */
36793367Smdodd    if ((env_top = getenv("TOP")) != NULL)
368106939Ssam    {
3697055Sdg	av = preset_argv = argparse(env_top, &preset_argc);
370112308Smdodd	ac = preset_argc;
371112308Smdodd
372112308Smdodd	/* set the dummy argument to an explanatory message, in case
373112308Smdodd	   getopt encounters a bad argument */
374112308Smdodd	preset_argv[0] = "while processing environment";
375112308Smdodd    }
376112308Smdodd
377112308Smdodd    /* process options */
378112308Smdodd    do {
379112308Smdodd	/* if we're done doing the presets, then process the real arguments */
380112308Smdodd	if (preset_argc == 0)
381112308Smdodd	{
382112308Smdodd	    ac = argc;
383112308Smdodd	    av = argv;
384112308Smdodd
385112308Smdodd	    /* this should keep getopt happy... */
386112308Smdodd	    optind = 1;
387112308Smdodd	}
388112308Smdodd
389112308Smdodd	while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:tw")) != EOF)
390112308Smdodd	{
391112308Smdodd	    switch(i)
392106939Ssam	    {
393106939Ssam	      case 'v':			/* show version number */
39493379Smdodd		fprintf(stderr, "%s: version %s\n",
39593379Smdodd			myname, version_string());
39693379Smdodd		exit(1);
397148887Srwatson		break;
398148887Srwatson
39993379Smdodd	      case 'u':			/* toggle uid/username display */
40093379Smdodd		do_unames = !do_unames;
401112308Smdodd		break;
402112308Smdodd
403112308Smdodd	      case 'U':			/* display only username's processes */
404112308Smdodd		if ((ps.uid[0] = userid(optarg)) == -1)
405112308Smdodd		{
406112308Smdodd		    fprintf(stderr, "%s: unknown user\n", optarg);
407112308Smdodd		    exit(1);
408112308Smdodd		}
409112308Smdodd		break;
410112308Smdodd
411112308Smdodd	      case 'S':			/* show system processes */
412112308Smdodd		ps.system = !ps.system;
413112308Smdodd		break;
414105577Srwatson
415172930Srwatson	      case 'I':                   /* show idle processes */
416105577Srwatson		ps.idle = !ps.idle;
417105577Srwatson		break;
41893379Smdodd
419112287Smdodd	      case 'i':			/* go interactive regardless */
420112287Smdodd		interactive = Yes;
421112287Smdodd		break;
422112287Smdodd
423112287Smdodd	      case 'n':			/* batch, or non-interactive */
424112287Smdodd	      case 'b':
42593379Smdodd		interactive = No;
42693379Smdodd		break;
42793379Smdodd
42893379Smdodd	      case 'a':
429152315Sru		fmt_flags ^= FMT_SHOWARGS;
43093379Smdodd		break;
43193379Smdodd
43293379Smdodd	      case 'd':			/* number of displays to show */
43393379Smdodd		if ((i = atoiwi(optarg)) == Invalid || i == 0)
43493379Smdodd		{
43593379Smdodd		    fprintf(stderr,
43621830Sjoerg			"%s: warning: display count should be positive -- option ignored\n",
437121436Simp			myname);
438121436Simp		    warnings++;
43921830Sjoerg		}
44021830Sjoerg		else
44121830Sjoerg		{
4427055Sdg		    displays = i;
44321830Sjoerg		}
4447055Sdg		break;
44521830Sjoerg
44621830Sjoerg	      case 's':
44721830Sjoerg		if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0))
44821830Sjoerg		{
44921830Sjoerg		    fprintf(stderr,
45021830Sjoerg			"%s: warning: seconds delay should be positive -- using default\n",
45121830Sjoerg			myname);
45221830Sjoerg		    delay = Default_DELAY;
45321830Sjoerg		    warnings++;
45421830Sjoerg		}
455106939Ssam		break;
456111790Smdodd
457106939Ssam	      case 'q':		/* be quick about it */
458111790Smdodd		/* only allow this if user is really root */
45993379Smdodd		if (getuid() == 0)
46093379Smdodd		{
46193379Smdodd		    /* be very un-nice! */
46293379Smdodd		    (void) nice(-20);
4637055Sdg		}
46493377Smdodd		else
4657055Sdg		{
4667055Sdg		    fprintf(stderr,
4677055Sdg			"%s: warning: `-q' option can only be used by root\n",
46821830Sjoerg			myname);
469112266Smdodd		    warnings++;
470112266Smdodd		}
47193379Smdodd		break;
4727055Sdg
47393379Smdodd	      case 'm':		/* select display mode */
47421830Sjoerg		if (strcmp(optarg, "io") == 0) {
475128396Sluigi			displaymode = DISP_IO;
476111888Sjlemon		} else if (strcmp(optarg, "cpu") == 0) {
477111888Sjlemon			displaymode = DISP_CPU;
478111888Sjlemon		} else {
479111888Sjlemon			fprintf(stderr,
480111888Sjlemon			"%s: warning: `-m' option can only take args "
48121830Sjoerg			"'io' or 'cpu'\n",
48221830Sjoerg			myname);
483128396Sluigi			exit(1);
484111888Sjlemon		}
485111888Sjlemon		break;
486111888Sjlemon
487111888Sjlemon	      case 'o':		/* select sort order */
488111888Sjlemon#ifdef ORDER
48921830Sjoerg		order_name = optarg;
49021830Sjoerg#else
49193377Smdodd		fprintf(stderr,
49293377Smdodd			"%s: this platform does not support arbitrary ordering.  Sorry.\n",
49393379Smdodd			myname);
49493379Smdodd		warnings++;
4957055Sdg#endif
49693379Smdodd		break;
49793377Smdodd
49821830Sjoerg	      case 't':
49993377Smdodd		ps.self = (ps.self == -1) ? getpid() : -1;
50093377Smdodd		break;
50121830Sjoerg
5027055Sdg	      case 'C':
5037055Sdg		ps.wcpu = !ps.wcpu;
504295896Sgnn		break;
505295896Sgnn
506111888Sjlemon	      case 'H':
5077055Sdg		ps.thread = !ps.thread;
5087055Sdg		break;
5097055Sdg
51078295Sjlemon	      case 'j':
51178295Sjlemon		ps.jail = !ps.jail;
512111888Sjlemon		break;
5137055Sdg
5147055Sdg	      case 'J':			/* display only jail's processes */
51554263Sshin		if ((ps.jid = jail_getid(optarg)) == -1)
51654263Sshin		{
517111888Sjlemon		    fprintf(stderr, "%s: unknown jail\n", optarg);
51854263Sshin		    exit(1);
51954263Sshin		}
52021830Sjoerg		ps.jail = 1;
52121830Sjoerg		break;
522111888Sjlemon
52321830Sjoerg	      case 'P':
52421830Sjoerg		pcpu_stats = !pcpu_stats;
5257055Sdg		break;
52621830Sjoerg
527111888Sjlemon	      case 'w':
5287055Sdg		ps.swap = 1;
5297055Sdg		break;
53021830Sjoerg
53121830Sjoerg	      case 'z':
532111888Sjlemon		ps.kidle = !ps.kidle;
53321830Sjoerg		break;
53421830Sjoerg
535111888Sjlemon	      default:
536111888Sjlemon		fprintf(stderr,
53721830Sjoerg"Top version %s\n"
5387055Sdg"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n"
53921830Sjoerg"       [-J jail] [-U username] [number]\n",
5407055Sdg			version_string(), myname);
5417055Sdg		exit(1);
5427055Sdg	    }
5437055Sdg	}
5447055Sdg
54521830Sjoerg	/* get count of top processes to display (if any) */
5467055Sdg	if (optind < ac)
54721830Sjoerg	{
5487055Sdg	    if ((topn = atoiwi(av[optind])) == Invalid)
54993377Smdodd	    {
5507055Sdg		fprintf(stderr,
551223741Sbz			"%s: warning: process display count should be non-negative -- using default\n",
552111888Sjlemon			myname);
55393377Smdodd		warnings++;
55493377Smdodd	    }
55593377Smdodd#if Default_TOPN == Infinity
55693377Smdodd            else
55793377Smdodd	    {
55893377Smdodd		topn_specified = Yes;
55993377Smdodd	    }
5607055Sdg#endif
56193380Smdodd	}
5627055Sdg
5637055Sdg	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
5647055Sdg	i = preset_argc;
5657055Sdg	preset_argc = 0;
566152296Sru
56793367Smdodd    /* repeat only if we really did the preset arguments */
568152296Sru    } while (i != 0);
56993383Smdodd
5707055Sdg    /* set constants for username/uid display correctly */
57193367Smdodd    if (!do_unames)
57293367Smdodd    {
5737055Sdg	uname_field = "   UID  ";
5747055Sdg	get_userid = itoa7;
57593373Smdodd    }
5767055Sdg
57793379Smdodd    /* initialize the kernel memory interface */
57893379Smdodd    if (machine_init(&statics, do_unames) == -1)
57993379Smdodd    {
5807055Sdg	exit(1);
58193379Smdodd    }
582106939Ssam
58368180Sume#ifdef ORDER
58493379Smdodd    /* determine sorting order index, if necessary */
58516063Sgpalmer    if (order_name != NULL)
58621830Sjoerg    {
58721830Sjoerg	if ((order_index = string_index(order_name, statics.order_names)) == -1)
58821830Sjoerg	{
589152315Sru	    char **pp;
590152315Sru
59193379Smdodd	    fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
59221831Sjoerg		    myname, order_name);
59321831Sjoerg	    fprintf(stderr, "\tTry one of these:");
59421831Sjoerg	    pp = statics.order_names;
595152296Sru	    while (*pp != NULL)
59693379Smdodd	    {
59793383Smdodd		fprintf(stderr, " %s", *pp++);
59893383Smdodd	    }
59993383Smdodd	    fputc('\n', stderr);
60093379Smdodd	    exit(1);
6017055Sdg	}
60268180Sume    }
60393382Smdodd#endif
60493382Smdodd
60593382Smdodd#ifdef no_initialization_needed
60693382Smdodd    /* initialize the hashing stuff */
60793382Smdodd    if (do_unames)
60893382Smdodd    {
60993382Smdodd	init_hash();
61093382Smdodd    }
61193382Smdodd#endif
61293382Smdodd
61393382Smdodd    /* initialize termcap */
61493382Smdodd    init_termcap(interactive);
61593382Smdodd
61693382Smdodd    /* get the string to use for the process area header */
61793382Smdodd    header_text = format_header(uname_field);
61893382Smdodd
61993382Smdodd    /* initialize display interface */
620194581Srdivacky    if ((max_topn = display_init(&statics)) == -1)
62193382Smdodd    {
62293382Smdodd	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
62393382Smdodd	exit(4);
62493382Smdodd    }
62593382Smdodd
62693382Smdodd    /* print warning if user requested more processes than we can display */
62793382Smdodd    if (topn > max_topn)
62893382Smdodd    {
62993382Smdodd	fprintf(stderr,
63093382Smdodd		"%s: warning: this terminal can only display %d processes.\n",
63193382Smdodd		myname, max_topn);
63293382Smdodd	warnings++;
63393382Smdodd    }
63493382Smdodd
63593382Smdodd    /* adjust for topn == Infinity */
63693382Smdodd    if (topn == Infinity)
63793382Smdodd    {
63893382Smdodd	/*
63993382Smdodd	 *  For smart terminals, infinity really means everything that can
64093382Smdodd	 *  be displayed, or Largest.
64193382Smdodd	 *  On dumb terminals, infinity means every process in the system!
64293382Smdodd	 *  We only really want to do that if it was explicitly specified.
64393382Smdodd	 *  This is always the case when "Default_TOPN != Infinity".  But if
64493382Smdodd	 *  topn wasn't explicitly specified and we are on a dumb terminal
64593382Smdodd	 *  and the default is Infinity, then (and only then) we use
64693382Smdodd	 *  "Nominal_TOPN" instead.
64793382Smdodd	 */
64893382Smdodd#if Default_TOPN == Infinity
64993382Smdodd	topn = smart_terminal ? Largest :
65093382Smdodd		    (topn_specified ? Largest : Nominal_TOPN);
65193382Smdodd#else
65293382Smdodd	topn = Largest;
653152315Sru#endif
65493382Smdodd    }
65593382Smdodd
656152315Sru    /* set header display accordingly */
657147256Sbrooks    display_header(topn > 0);
65893382Smdodd
65993382Smdodd    /* determine interactive state */
66093382Smdodd    if (interactive == Maybe)
66193382Smdodd    {
66293382Smdodd	interactive = smart_terminal;
66393382Smdodd    }
66493382Smdodd
66593382Smdodd    /* if # of displays not specified, fill it in */
66693382Smdodd    if (displays == 0)
66793382Smdodd    {
66893382Smdodd	displays = smart_terminal ? Infinity : 1;
66993382Smdodd    }
670104302Sphk
671144045Smdodd    /* hold interrupt signals while setting up the screen and the handlers */
67293382Smdodd#ifdef SIGHOLD
67393382Smdodd    sighold(SIGINT);
67493382Smdodd    sighold(SIGQUIT);
67593382Smdodd    sighold(SIGTSTP);
676152315Sru#else
67793382Smdodd    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
67893382Smdodd#endif
67993382Smdodd    init_screen();
68093382Smdodd    (void) signal(SIGINT, leave);
68193382Smdodd    (void) signal(SIGQUIT, leave);
68293382Smdodd    (void) signal(SIGTSTP, tstop);
68393382Smdodd#ifdef SIGWINCH
68493382Smdodd    (void) signal(SIGWINCH, winch);
68593382Smdodd#endif
68693382Smdodd#ifdef SIGRELSE
68793382Smdodd    sigrelse(SIGINT);
68893382Smdodd    sigrelse(SIGQUIT);
68993382Smdodd    sigrelse(SIGTSTP);
69093382Smdodd#else
69193382Smdodd    (void) sigsetmask(old_sigmask);
692144045Smdodd#endif
69393382Smdodd    if (warnings)
69493382Smdodd    {
69593382Smdodd	fputs("....", stderr);
69693382Smdodd	fflush(stderr);			/* why must I do this? */
69793382Smdodd	sleep((unsigned)(3 * warnings));
69893382Smdodd	fputc('\n', stderr);
69968180Sume    }
70068180Sume
70168180Sumerestart:
70268180Sume
70368180Sume    /*
70468180Sume     *  main loop -- repeat while display count is positive or while it
70568180Sume     *		indicates infinity (by being -1)
706184709Sbz     */
70768180Sume
708184709Sbz    while ((displays == -1) || (displays-- > 0))
70968180Sume    {
71068180Sume	int (*compare)();
71168180Sume
71268180Sume
71368180Sume	/* get the current stats */
71468180Sume	get_system_info(&system_info);
71568180Sume
71668180Sume#ifdef ORDER
71768180Sume	compare = compares[order_index];
71868180Sume#else
71968180Sume	if (displaymode == DISP_CPU)
72068180Sume		compare = proc_compare;
72168180Sume	else
72293369Smdodd		compare = io_compare;
72368180Sume#endif
72493369Smdodd
72568180Sume	/* get the current set of processes */
72668180Sume	processes =
72768180Sume		get_process_info(&system_info, &ps, compare);
72868180Sume
72968180Sume	/* display the load averages */
73093369Smdodd	(*d_loadave)(system_info.last_pid,
731184205Sdes		     system_info.load_avg);
732148641Srwatson
733148641Srwatson	/* display the current time */
734148641Srwatson	/* this method of getting the time SHOULD be fairly portable */
73568180Sume	time(&curr_time);
73668180Sume	i_uptime(&system_info.boottime, &curr_time);
73768180Sume	i_timeofday(&curr_time);
73868180Sume
73968180Sume	/* display process state breakdown */
74093375Smdodd	(*d_procstates)(system_info.p_total,
74168180Sume			system_info.procstates);
74268180Sume
74368180Sume	/* display the cpu state percentage breakdown */
74468180Sume	if (dostates)	/* but not the first time */
74593369Smdodd	{
74668180Sume	    (*d_cpustates)(system_info.cpustates);
74768180Sume	}
74868180Sume	else
74968180Sume	{
75068180Sume	    /* we'll do it next time */
75168180Sume	    if (smart_terminal)
75268180Sume	    {
75368180Sume		z_cpustates();
75468180Sume	    }
75568180Sume	    else
75668180Sume	    {
75768180Sume		putchar('\n');
75893369Smdodd	    }
75968180Sume	    dostates = Yes;
76068180Sume	}
76193369Smdodd
762184205Sdes	/* display memory stats */
763148641Srwatson	(*d_memory)(system_info.memory);
764148641Srwatson	(*d_arc)(system_info.arc);
765148641Srwatson	(*d_carc)(system_info.carc);
76668180Sume
76768180Sume	/* display swap stats */
76868180Sume	(*d_swap)(system_info.swap);
76968180Sume
77068180Sume	/* handle message area */
77193375Smdodd	(*d_message)();
77268180Sume
77368180Sume	/* update the header area */
77468180Sume	(*d_header)(header_text);
77568180Sume
77693369Smdodd	if (topn > 0)
77768180Sume	{
77868180Sume	    /* determine number of processes to actually display */
77968180Sume	    /* this number will be the smallest of:  active processes,
78068180Sume	       number user requested, number current screen accomodates */
78168180Sume	    active_procs = system_info.P_ACTIVE;
78268180Sume	    if (active_procs > topn)
78368180Sume	    {
78493369Smdodd		active_procs = topn;
78568180Sume	    }
78693375Smdodd	    if (active_procs > max_topn)
78793375Smdodd	    {
78868180Sume		active_procs = max_topn;
78993375Smdodd	    }
79093375Smdodd
79193375Smdodd	    /* now show the top "n" processes. */
79293375Smdodd	    for (i = 0; i < active_procs; i++)
793241394Skevlo	    {
79493375Smdodd		(*d_process)(i, format_next_process(processes, get_userid,
79593375Smdodd			     fmt_flags));
79693375Smdodd	    }
79793375Smdodd	}
798	else
799	{
800	    i = 0;
801	}
802
803	/* do end-screen processing */
804	u_endscreen(i);
805
806	/* now, flush the output buffer */
807	if (fflush(stdout) != 0)
808	{
809	    new_message(MT_standout, " Write error on stdout");
810	    putchar('\r');
811	    quit(1);
812	    /*NOTREACHED*/
813	}
814
815	/* only do the rest if we have more displays to show */
816	if (displays)
817	{
818	    /* switch out for new display on smart terminals */
819	    if (smart_terminal)
820	    {
821		if (overstrike)
822		{
823		    reset_display();
824		}
825		else
826		{
827		    d_loadave = u_loadave;
828		    d_procstates = u_procstates;
829		    d_cpustates = u_cpustates;
830		    d_memory = u_memory;
831		    d_arc = u_arc;
832		    d_carc = u_carc;
833		    d_swap = u_swap;
834		    d_message = u_message;
835		    d_header = u_header;
836		    d_process = u_process;
837		}
838	    }
839
840	    no_command = Yes;
841	    if (!interactive)
842	    {
843		sleep(delay);
844		if (leaveflag) {
845		    end_screen();
846		    exit(0);
847		}
848	    }
849	    else while (no_command)
850	    {
851		/* assume valid command unless told otherwise */
852		no_command = No;
853
854		/* set up arguments for select with timeout */
855		FD_ZERO(&readfds);
856		FD_SET(0, &readfds);		/* for standard input */
857		timeout.tv_sec  = delay;
858		timeout.tv_usec = 0;
859
860		if (leaveflag) {
861		    end_screen();
862		    exit(0);
863		}
864
865		if (tstopflag) {
866		    /* move to the lower left */
867		    end_screen();
868		    fflush(stdout);
869
870		    /* default the signal handler action */
871		    (void) signal(SIGTSTP, SIG_DFL);
872
873		    /* unblock the signal and send ourselves one */
874#ifdef SIGRELSE
875		    sigrelse(SIGTSTP);
876#else
877		    (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
878#endif
879		    (void) kill(0, SIGTSTP);
880
881		    /* reset the signal handler */
882		    (void) signal(SIGTSTP, tstop);
883
884		    /* reinit screen */
885		    reinit_screen();
886		    reset_display();
887		    tstopflag = 0;
888		    goto restart;
889		}
890
891		if (winchflag) {
892		    /* reascertain the screen dimensions */
893		    get_screensize();
894
895		    /* tell display to resize */
896		    max_topn = display_resize();
897
898		    /* reset the signal handler */
899		    (void) signal(SIGWINCH, winch);
900
901		    reset_display();
902		    winchflag = 0;
903		    goto restart;
904		}
905
906		/* wait for either input or the end of the delay period */
907		sel_ret = select(2, &readfds, NULL, NULL, &timeout);
908		if (sel_ret < 0 && errno != EINTR)
909		    quit(0);
910		if (sel_ret > 0)
911		{
912		    int newval;
913		    char *errmsg;
914
915		    /* something to read -- clear the message area first */
916		    clear_message();
917
918		    /* now read it and convert to command strchr */
919		    /* (use "change" as a temporary to hold strchr) */
920		    if (read(0, &ch, 1) != 1)
921		    {
922			/* read error: either 0 or -1 */
923			new_message(MT_standout, " Read error on stdin");
924			putchar('\r');
925			quit(1);
926			/*NOTREACHED*/
927		    }
928		    if ((iptr = strchr(command_chars, ch)) == NULL)
929		    {
930			if (ch != '\r' && ch != '\n')
931			{
932			    /* illegal command */
933			    new_message(MT_standout, " Command not understood");
934			}
935			putchar('\r');
936			no_command = Yes;
937		    }
938		    else
939		    {
940			change = iptr - command_chars;
941			if (overstrike && change > CMD_OSLIMIT)
942			{
943			    /* error */
944			    new_message(MT_standout,
945			    " Command cannot be handled by this terminal");
946			    putchar('\r');
947			    no_command = Yes;
948			}
949			else switch(change)
950			{
951			    case CMD_redraw:	/* redraw screen */
952				reset_display();
953				break;
954
955			    case CMD_update:	/* merely update display */
956				/* is the load average high? */
957				if (system_info.load_avg[0] > LoadMax)
958				{
959				    /* yes, go home for visual feedback */
960				    go_home();
961				    fflush(stdout);
962				}
963				break;
964
965			    case CMD_quit:	/* quit */
966				quit(0);
967				/*NOTREACHED*/
968				break;
969
970			    case CMD_help1:	/* help */
971			    case CMD_help2:
972				reset_display();
973				clear();
974				show_help();
975				standout("Hit any key to continue: ");
976				fflush(stdout);
977				(void) read(0, &ch, 1);
978				break;
979
980			    case CMD_errors:	/* show errors */
981				if (error_count() == 0)
982				{
983				    new_message(MT_standout,
984					" Currently no errors to report.");
985				    putchar('\r');
986				    no_command = Yes;
987				}
988				else
989				{
990				    reset_display();
991				    clear();
992				    show_errors();
993				    standout("Hit any key to continue: ");
994				    fflush(stdout);
995				    (void) read(0, &ch, 1);
996				}
997				break;
998
999			    case CMD_number1:	/* new number */
1000			    case CMD_number2:
1001				new_message(MT_standout,
1002				    "Number of processes to show: ");
1003				newval = readline(tempbuf1, 8, Yes);
1004				if (newval > -1)
1005				{
1006				    if (newval > max_topn)
1007				    {
1008					new_message(MT_standout | MT_delayed,
1009					  " This terminal can only display %d processes.",
1010					  max_topn);
1011					putchar('\r');
1012				    }
1013
1014				    if (newval == 0)
1015				    {
1016					/* inhibit the header */
1017					display_header(No);
1018				    }
1019				    else if (newval > topn && topn == 0)
1020				    {
1021					/* redraw the header */
1022					display_header(Yes);
1023					d_header = i_header;
1024				    }
1025				    topn = newval;
1026				}
1027				break;
1028
1029			    case CMD_delay:	/* new seconds delay */
1030				new_message(MT_standout, "Seconds to delay: ");
1031				if ((i = readline(tempbuf1, 8, Yes)) > -1)
1032				{
1033				    if ((delay = i) == 0 && getuid() != 0)
1034				    {
1035					delay = 1;
1036				    }
1037				}
1038				clear_message();
1039				break;
1040
1041			    case CMD_displays:	/* change display count */
1042				new_message(MT_standout,
1043					"Displays to show (currently %s): ",
1044					displays == -1 ? "infinite" :
1045							 itoa(displays));
1046				if ((i = readline(tempbuf1, 10, Yes)) > 0)
1047				{
1048				    displays = i;
1049				}
1050				else if (i == 0)
1051				{
1052				    quit(0);
1053				}
1054				clear_message();
1055				break;
1056
1057			    case CMD_kill:	/* kill program */
1058				new_message(0, "kill ");
1059				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1060				{
1061				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
1062				    {
1063					new_message(MT_standout, "%s", errmsg);
1064					putchar('\r');
1065					no_command = Yes;
1066				    }
1067				}
1068				else
1069				{
1070				    clear_message();
1071				}
1072				break;
1073
1074			    case CMD_renice:	/* renice program */
1075				new_message(0, "renice ");
1076				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1077				{
1078				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
1079				    {
1080					new_message(MT_standout, "%s", errmsg);
1081					putchar('\r');
1082					no_command = Yes;
1083				    }
1084				}
1085				else
1086				{
1087				    clear_message();
1088				}
1089				break;
1090
1091			    case CMD_idletog:
1092			    case CMD_idletog2:
1093				ps.idle = !ps.idle;
1094				new_message(MT_standout | MT_delayed,
1095				    " %sisplaying idle processes.",
1096				    ps.idle ? "D" : "Not d");
1097				putchar('\r');
1098				break;
1099
1100			    case CMD_selftog:
1101				ps.self = (ps.self == -1) ? getpid() : -1;
1102				new_message(MT_standout | MT_delayed,
1103				    " %sisplaying self.",
1104				    (ps.self == -1) ? "D" : "Not d");
1105				putchar('\r');
1106				break;
1107
1108			    case CMD_user:
1109				if (handle_user(tempbuf2, sizeof(tempbuf2)))
1110				    no_command = Yes;
1111				break;
1112
1113			    case CMD_thrtog:
1114				ps.thread = !ps.thread;
1115				new_message(MT_standout | MT_delayed,
1116				    " Displaying threads %s",
1117				    ps.thread ? "separately" : "as a count");
1118				header_text = format_header(uname_field);
1119				reset_display();
1120				putchar('\r');
1121				break;
1122			    case CMD_wcputog:
1123				ps.wcpu = !ps.wcpu;
1124				new_message(MT_standout | MT_delayed,
1125				    " Displaying %s CPU",
1126				    ps.wcpu ? "weighted" : "raw");
1127				header_text = format_header(uname_field);
1128				reset_display();
1129				putchar('\r');
1130				break;
1131			    case CMD_viewtog:
1132				if (++displaymode == DISP_MAX)
1133					displaymode = 0;
1134				header_text = format_header(uname_field);
1135				display_header(Yes);
1136				d_header = i_header;
1137				reset_display();
1138				break;
1139			    case CMD_viewsys:
1140				ps.system = !ps.system;
1141				break;
1142			    case CMD_showargs:
1143				fmt_flags ^= FMT_SHOWARGS;
1144				break;
1145#ifdef ORDER
1146			    case CMD_order:
1147				new_message(MT_standout,
1148				    "Order to sort: ");
1149				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1150				{
1151				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
1152					{
1153					  new_message(MT_standout,
1154					      " %s: unrecognized sorting order", tempbuf2);
1155					  no_command = Yes;
1156				    }
1157				    else
1158				    {
1159					order_index = i;
1160				    }
1161				    putchar('\r');
1162				}
1163				else
1164				{
1165				    clear_message();
1166				}
1167				break;
1168#endif
1169			    case CMD_jidtog:
1170				ps.jail = !ps.jail;
1171				new_message(MT_standout | MT_delayed,
1172				    " %sisplaying jail ID.",
1173				    ps.jail ? "D" : "Not d");
1174				header_text = format_header(uname_field);
1175				reset_display();
1176				putchar('\r');
1177				break;
1178
1179			    case CMD_jail:
1180				new_message(MT_standout,
1181				    "Jail to show (+ for all): ");
1182				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1183				{
1184				    if (tempbuf2[0] == '+' &&
1185					tempbuf2[1] == '\0')
1186				    {
1187					ps.jid = -1;
1188				    }
1189				    else if ((i = jail_getid(tempbuf2)) == -1)
1190				    {
1191					new_message(MT_standout,
1192					    " %s: unknown jail", tempbuf2);
1193					no_command = Yes;
1194				    }
1195				    else
1196				    {
1197					ps.jid = i;
1198				    }
1199				    if (ps.jail == 0) {
1200					    ps.jail = 1;
1201					    new_message(MT_standout |
1202						MT_delayed, " Displaying jail "
1203						"ID.");
1204					    header_text =
1205						format_header(uname_field);
1206					    reset_display();
1207				    }
1208				    putchar('\r');
1209				}
1210				else
1211				{
1212				    clear_message();
1213				}
1214				break;
1215
1216			    case CMD_kidletog:
1217				ps.kidle = !ps.kidle;
1218				new_message(MT_standout | MT_delayed,
1219				    " %sisplaying system idle process.",
1220				    ps.kidle ? "D" : "Not d");
1221				putchar('\r');
1222				break;
1223			    case CMD_pcputog:
1224				pcpu_stats = !pcpu_stats;
1225				new_message(MT_standout | MT_delayed,
1226				    " Displaying %sCPU statistics.",
1227				    pcpu_stats ? "per-" : "global ");
1228				toggle_pcpustats();
1229				max_topn = display_updatecpus(&statics);
1230				reset_display();
1231				putchar('\r');
1232				break;
1233			    case CMD_swaptog:
1234				ps.swap = !ps.swap;
1235				new_message(MT_standout | MT_delayed,
1236				    " %sisplaying per-process swap usage.",
1237				    ps.swap ? "D" : "Not d");
1238				header_text = format_header(uname_field);
1239				reset_display();
1240				putchar('\r');
1241				break;
1242			    default:
1243				new_message(MT_standout, " BAD CASE IN SWITCH!");
1244				putchar('\r');
1245			}
1246		    }
1247
1248		    /* flush out stuff that may have been written */
1249		    fflush(stdout);
1250		}
1251	    }
1252	}
1253    }
1254
1255#ifdef DEBUG
1256    fclose(debug);
1257#endif
1258    quit(0);
1259    /*NOTREACHED*/
1260}
1261
1262/*
1263 *  reset_display() - reset all the display routine pointers so that entire
1264 *	screen will get redrawn.
1265 */
1266
1267void
1268reset_display()
1269
1270{
1271    d_loadave    = i_loadave;
1272    d_procstates = i_procstates;
1273    d_cpustates  = i_cpustates;
1274    d_memory     = i_memory;
1275    d_arc        = i_arc;
1276    d_carc       = i_carc;
1277    d_swap       = i_swap;
1278    d_message	 = i_message;
1279    d_header	 = i_header;
1280    d_process	 = i_process;
1281}
1282
1283/*
1284 *  signal handlers
1285 */
1286
1287sigret_t leave()	/* exit under normal conditions -- INT handler */
1288
1289{
1290    leaveflag = 1;
1291}
1292
1293sigret_t tstop(i)	/* SIGTSTP handler */
1294
1295int i;
1296
1297{
1298    tstopflag = 1;
1299}
1300
1301#ifdef SIGWINCH
1302sigret_t winch(i)		/* SIGWINCH handler */
1303
1304int i;
1305
1306{
1307    winchflag = 1;
1308}
1309#endif
1310
1311void quit(status)		/* exit under duress */
1312
1313int status;
1314
1315{
1316    end_screen();
1317    exit(status);
1318    /*NOTREACHED*/
1319}
1320