124139Sjoerg/*
224139Sjoerg *  Top users/processes display for Unix
324139Sjoerg *  Version 3
424139Sjoerg *
524139Sjoerg *  This program may be freely redistributed,
624139Sjoerg *  but this entire comment MUST remain intact.
724139Sjoerg *
824139Sjoerg *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
924139Sjoerg *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1089755Sdwmalone *
1189755Sdwmalone * $FreeBSD$
1224139Sjoerg */
1324139Sjoerg
1424139Sjoerg/*
1524139Sjoerg *  This file contains the routines that implement some of the interactive
1624139Sjoerg *  mode commands.  Note that some of the commands are implemented in-line
1724139Sjoerg *  in "main".  This is necessary because they change the global state of
1824139Sjoerg *  "top" (i.e.:  changing the number of processes to display).
1924139Sjoerg */
2024139Sjoerg
2124139Sjoerg#include "os.h"
22301836Sngie
2324139Sjoerg#include <sys/time.h>
2424139Sjoerg#include <sys/resource.h>
2524139Sjoerg
26301836Sngie#include <ctype.h>
27301836Sngie#include <errno.h>
28301836Sngie#include <signal.h>
29301836Sngie#include <unistd.h>
30301836Sngie
31301836Sngie#include "commands.h"
3224139Sjoerg#include "sigdesc.h"		/* generated automatically */
3337451Sbde#include "top.h"
3424139Sjoerg#include "boolean.h"
3524139Sjoerg#include "utils.h"
36301836Sngie#include "machine.h"
3724139Sjoerg
3824139Sjoergextern int  errno;
3924139Sjoerg
4024139Sjoergextern char *copyright;
4124139Sjoerg
4224139Sjoerg/* imported from screen.c */
4324139Sjoergextern int overstrike;
4424139Sjoerg
4524139Sjoergint err_compar();
4624139Sjoergchar *err_string();
47301836Sngiestatic int str_adderr(char *str, int len, int err);
48301836Sngiestatic int str_addarg(char *str, int len, char *arg, int first);
4924139Sjoerg
5024139Sjoerg/*
5124139Sjoerg *  show_help() - display the help screen; invoked in response to
5224139Sjoerg *		either 'h' or '?'.
5324139Sjoerg */
5424139Sjoerg
55301836Sngievoid
5624139Sjoergshow_help()
5724139Sjoerg
5824139Sjoerg{
5924139Sjoerg    printf("Top version %s, %s\n", version_string(), copyright);
6024139Sjoerg    fputs("\n\n\
6124139SjoergA top users display for Unix\n\
6224139Sjoerg\n\
6324139SjoergThese single-character commands are available:\n\
6424139Sjoerg\n\
6524139Sjoerg^L      - redraw screen\n\
6624139Sjoergq       - quit\n\
6724139Sjoergh or ?  - help; show this text\n", stdout);
6824139Sjoerg
6924139Sjoerg    /* not all commands are availalbe with overstrike terminals */
7024139Sjoerg    if (overstrike)
7124139Sjoerg    {
7224139Sjoerg	fputs("\n\
7324139SjoergOther commands are also available, but this terminal is not\n\
7424139Sjoergsophisticated enough to handle those commands gracefully.\n\n", stdout);
7524139Sjoerg    }
7624139Sjoerg    else
7724139Sjoerg    {
7824139Sjoerg	fputs("\
79146342SkeramidaC       - toggle the displaying of weighted CPU percentage\n\
8024139Sjoergd       - change number of displays to show\n\
8124139Sjoerge       - list errors generated by last \"kill\" or \"renice\" command\n\
82168799SrafanH       - toggle the displaying of threads\n\
83132038Salfredi or I  - toggle the displaying of idle processes\n\
84169257Srafanj       - toggle the displaying of jail ID\n\
85266280SbdreweryJ       - display processes for only one jail (+ selects all jails)\n\
8624139Sjoergk       - kill processes; send a signal to a list of processes\n\
87132038Salfredm       - toggle the display between 'cpu' and 'io' modes\n\
8824139Sjoergn or #  - change number of processes to display\n", stdout);
8924139Sjoerg#ifdef ORDER
90131829Skeramida	if (displaymode == DISP_CPU)
91131829Skeramida		fputs("\
92242888Srpauloo       - specify sort order (pri, size, res, cpu, time, threads, jid, pid)\n",
93215186Spluknet	    stdout);
94131829Skeramida	else
95131829Skeramida		fputs("\
96242888Srpauloo       - specify sort order (vcsw, ivcsw, read, write, fault, total, jid, pid)\n",
97215186Spluknet	    stdout);
9824139Sjoerg#endif
9924139Sjoerg	fputs("\
100223936SjhbP       - toggle the displaying of per-CPU statistics\n\
10124139Sjoergr       - renice a process\n\
10224139Sjoergs       - change number of seconds to delay between updates\n\
103132005SalfredS       - toggle the displaying of system processes\n\
104169237Sstasa       - toggle the displaying of process titles\n\
105132038Salfredt       - toggle the display of this process\n\
10624139Sjoergu       - display processes for only one user (+ selects all users)\n\
107222530Sjhbz       - toggle the displaying of the system idle process\n\
10824139Sjoerg\n\
10924139Sjoerg\n", stdout);
11024139Sjoerg    }
11124139Sjoerg}
11224139Sjoerg
11324139Sjoerg/*
11424139Sjoerg *  Utility routines that help with some of the commands.
11524139Sjoerg */
11624139Sjoerg
11724139Sjoergchar *next_field(str)
11824139Sjoerg
11924139Sjoergregister char *str;
12024139Sjoerg
12124139Sjoerg{
12224139Sjoerg    if ((str = strchr(str, ' ')) == NULL)
12324139Sjoerg    {
12424139Sjoerg	return(NULL);
12524139Sjoerg    }
12624139Sjoerg    *str = '\0';
12724139Sjoerg    while (*++str == ' ') /* loop */;
12824139Sjoerg
12924139Sjoerg    /* if there is nothing left of the string, return NULL */
13024139Sjoerg    /* This fix is dedicated to Greg Earle */
13124139Sjoerg    return(*str == '\0' ? NULL : str);
13224139Sjoerg}
13324139Sjoerg
134301836Sngieint
13524139Sjoergscanint(str, intp)
13624139Sjoerg
13724139Sjoergchar *str;
13824139Sjoergint  *intp;
13924139Sjoerg
14024139Sjoerg{
14124139Sjoerg    register int val = 0;
14224139Sjoerg    register char ch;
14324139Sjoerg
14424139Sjoerg    /* if there is nothing left of the string, flag it as an error */
14524139Sjoerg    /* This fix is dedicated to Greg Earle */
14624139Sjoerg    if (*str == '\0')
14724139Sjoerg    {
14824139Sjoerg	return(-1);
14924139Sjoerg    }
15024139Sjoerg
15124139Sjoerg    while ((ch = *str++) != '\0')
15224139Sjoerg    {
15324139Sjoerg	if (isdigit(ch))
15424139Sjoerg	{
15524139Sjoerg	    val = val * 10 + (ch - '0');
15624139Sjoerg	}
15724139Sjoerg	else if (isspace(ch))
15824139Sjoerg	{
15924139Sjoerg	    break;
16024139Sjoerg	}
16124139Sjoerg	else
16224139Sjoerg	{
16324139Sjoerg	    return(-1);
16424139Sjoerg	}
16524139Sjoerg    }
16624139Sjoerg    *intp = val;
16724139Sjoerg    return(0);
16824139Sjoerg}
16924139Sjoerg
17024139Sjoerg/*
17124139Sjoerg *  Some of the commands make system calls that could generate errors.
17224139Sjoerg *  These errors are collected up in an array of structures for later
17324139Sjoerg *  contemplation and display.  Such routines return a string containing an
17424139Sjoerg *  error message, or NULL if no errors occurred.  The next few routines are
17524139Sjoerg *  for manipulating and displaying these errors.  We need an upper limit on
17624139Sjoerg *  the number of errors, so we arbitrarily choose 20.
17724139Sjoerg */
17824139Sjoerg
17924139Sjoerg#define ERRMAX 20
18024139Sjoerg
18124139Sjoergstruct errs		/* structure for a system-call error */
18224139Sjoerg{
18389755Sdwmalone    int  errnum;	/* value of errno (that is, the actual error) */
18424139Sjoerg    char *arg;		/* argument that caused the error */
18524139Sjoerg};
18624139Sjoerg
18724139Sjoergstatic struct errs errs[ERRMAX];
18824139Sjoergstatic int errcnt;
18924139Sjoergstatic char *err_toomany = " too many errors occurred";
19024139Sjoergstatic char *err_listem =
19124139Sjoerg	" Many errors occurred.  Press `e' to display the list of errors.";
19224139Sjoerg
19324139Sjoerg/* These macros get used to reset and log the errors */
19424139Sjoerg#define ERR_RESET   errcnt = 0
19524139Sjoerg#define ERROR(p, e) if (errcnt >= ERRMAX) \
19624139Sjoerg		    { \
19724139Sjoerg			return(err_toomany); \
19824139Sjoerg		    } \
19924139Sjoerg		    else \
20024139Sjoerg		    { \
20124139Sjoerg			errs[errcnt].arg = (p); \
20289755Sdwmalone			errs[errcnt++].errnum = (e); \
20324139Sjoerg		    }
20424139Sjoerg
20524139Sjoerg/*
20624139Sjoerg *  err_string() - return an appropriate error string.  This is what the
20724139Sjoerg *	command will return for displaying.  If no errors were logged, then
20824139Sjoerg *	return NULL.  The maximum length of the error string is defined by
20924139Sjoerg *	"STRMAX".
21024139Sjoerg */
21124139Sjoerg
21224139Sjoerg#define STRMAX 80
21324139Sjoerg
21424139Sjoergchar *err_string()
21524139Sjoerg
21624139Sjoerg{
21724139Sjoerg    register struct errs *errp;
21824139Sjoerg    register int  cnt = 0;
21924139Sjoerg    register int  first = Yes;
22024139Sjoerg    register int  currerr = -1;
22124139Sjoerg    int stringlen;		/* characters still available in "string" */
22224139Sjoerg    static char string[STRMAX];
22324139Sjoerg
22424139Sjoerg    /* if there are no errors, return NULL */
22524139Sjoerg    if (errcnt == 0)
22624139Sjoerg    {
22724139Sjoerg	return(NULL);
22824139Sjoerg    }
22924139Sjoerg
23024139Sjoerg    /* sort the errors */
23124139Sjoerg    qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
23224139Sjoerg
23324139Sjoerg    /* need a space at the front of the error string */
23424139Sjoerg    string[0] = ' ';
23524139Sjoerg    string[1] = '\0';
23624139Sjoerg    stringlen = STRMAX - 2;
23724139Sjoerg
23824139Sjoerg    /* loop thru the sorted list, building an error string */
23924139Sjoerg    while (cnt < errcnt)
24024139Sjoerg    {
24124139Sjoerg	errp = &(errs[cnt++]);
24289755Sdwmalone	if (errp->errnum != currerr)
24324139Sjoerg	{
24424139Sjoerg	    if (currerr != -1)
24524139Sjoerg	    {
24624139Sjoerg		if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
24724139Sjoerg		{
24824139Sjoerg		    return(err_listem);
24924139Sjoerg		}
25024139Sjoerg		(void) strcat(string, "; ");	  /* we know there's more */
25124139Sjoerg	    }
25289755Sdwmalone	    currerr = errp->errnum;
25324139Sjoerg	    first = Yes;
25424139Sjoerg	}
25524139Sjoerg	if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
25624139Sjoerg	{
25724139Sjoerg	    return(err_listem);
25824139Sjoerg	}
25924139Sjoerg	first = No;
26024139Sjoerg    }
26124139Sjoerg
26224139Sjoerg    /* add final message */
26324139Sjoerg    stringlen = str_adderr(string, stringlen, currerr);
26424139Sjoerg
26524139Sjoerg    /* return the error string */
26624139Sjoerg    return(stringlen == 0 ? err_listem : string);
26724139Sjoerg}
26824139Sjoerg
26924139Sjoerg/*
27024139Sjoerg *  str_adderr(str, len, err) - add an explanation of error "err" to
27124139Sjoerg *	the string "str".
27224139Sjoerg */
27324139Sjoerg
274301836Sngiestatic int
27524139Sjoergstr_adderr(str, len, err)
27624139Sjoerg
27724139Sjoergchar *str;
27824139Sjoergint len;
27924139Sjoergint err;
28024139Sjoerg
28124139Sjoerg{
28224139Sjoerg    register char *msg;
28324139Sjoerg    register int  msglen;
28424139Sjoerg
28524139Sjoerg    msg = err == 0 ? "Not a number" : errmsg(err);
28624139Sjoerg    msglen = strlen(msg) + 2;
28724139Sjoerg    if (len <= msglen)
28824139Sjoerg    {
28924139Sjoerg	return(0);
29024139Sjoerg    }
29124139Sjoerg    (void) strcat(str, ": ");
29224139Sjoerg    (void) strcat(str, msg);
29324139Sjoerg    return(len - msglen);
29424139Sjoerg}
29524139Sjoerg
29624139Sjoerg/*
29724139Sjoerg *  str_addarg(str, len, arg, first) - add the string argument "arg" to
29824139Sjoerg *	the string "str".  This is the first in the group when "first"
29924139Sjoerg *	is set (indicating that a comma should NOT be added to the front).
30024139Sjoerg */
30124139Sjoerg
302301836Sngiestatic int
30324139Sjoergstr_addarg(str, len, arg, first)
30424139Sjoerg
30524139Sjoergchar *str;
30624139Sjoergint  len;
30724139Sjoergchar *arg;
30824139Sjoergint  first;
30924139Sjoerg
31024139Sjoerg{
31124139Sjoerg    register int arglen;
31224139Sjoerg
31324139Sjoerg    arglen = strlen(arg);
31424139Sjoerg    if (!first)
31524139Sjoerg    {
31624139Sjoerg	arglen += 2;
31724139Sjoerg    }
31824139Sjoerg    if (len <= arglen)
31924139Sjoerg    {
32024139Sjoerg	return(0);
32124139Sjoerg    }
32224139Sjoerg    if (!first)
32324139Sjoerg    {
32424139Sjoerg	(void) strcat(str, ", ");
32524139Sjoerg    }
32624139Sjoerg    (void) strcat(str, arg);
32724139Sjoerg    return(len - arglen);
32824139Sjoerg}
32924139Sjoerg
33024139Sjoerg/*
33124139Sjoerg *  err_compar(p1, p2) - comparison routine used by "qsort"
33224139Sjoerg *	for sorting errors.
33324139Sjoerg */
33424139Sjoerg
335301836Sngieint
33624139Sjoergerr_compar(p1, p2)
33724139Sjoerg
33824139Sjoergregister struct errs *p1, *p2;
33924139Sjoerg
34024139Sjoerg{
34124139Sjoerg    register int result;
34224139Sjoerg
34389755Sdwmalone    if ((result = p1->errnum - p2->errnum) == 0)
34424139Sjoerg    {
34524139Sjoerg	return(strcmp(p1->arg, p2->arg));
34624139Sjoerg    }
34724139Sjoerg    return(result);
34824139Sjoerg}
34924139Sjoerg
35024139Sjoerg/*
35124139Sjoerg *  error_count() - return the number of errors currently logged.
35224139Sjoerg */
35324139Sjoerg
354301836Sngieint
35524139Sjoergerror_count()
35624139Sjoerg
35724139Sjoerg{
35824139Sjoerg    return(errcnt);
35924139Sjoerg}
36024139Sjoerg
36124139Sjoerg/*
36224139Sjoerg *  show_errors() - display on stdout the current log of errors.
36324139Sjoerg */
36424139Sjoerg
365301836Sngievoid
36624139Sjoergshow_errors()
36724139Sjoerg
36824139Sjoerg{
36924139Sjoerg    register int cnt = 0;
37024139Sjoerg    register struct errs *errp = errs;
37124139Sjoerg
37224139Sjoerg    printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
37324139Sjoerg    while (cnt++ < errcnt)
37424139Sjoerg    {
37524139Sjoerg	printf("%5s: %s\n", errp->arg,
37689755Sdwmalone	    errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum));
37724139Sjoerg	errp++;
37824139Sjoerg    }
37924139Sjoerg}
38024139Sjoerg
38124139Sjoerg/*
38224139Sjoerg *  kill_procs(str) - send signals to processes, much like the "kill"
38324139Sjoerg *		command does; invoked in response to 'k'.
38424139Sjoerg */
38524139Sjoerg
38624139Sjoergchar *kill_procs(str)
38724139Sjoerg
38824139Sjoergchar *str;
38924139Sjoerg
39024139Sjoerg{
39124139Sjoerg    register char *nptr;
39224139Sjoerg    int signum = SIGTERM;	/* default */
39324139Sjoerg    int procnum;
39424139Sjoerg    struct sigdesc *sigp;
39524139Sjoerg    int uid;
39624139Sjoerg
39724139Sjoerg    /* reset error array */
39824139Sjoerg    ERR_RESET;
39924139Sjoerg
40024139Sjoerg    /* remember our uid */
40124139Sjoerg    uid = getuid();
40224139Sjoerg
40324139Sjoerg    /* skip over leading white space */
40424139Sjoerg    while (isspace(*str)) str++;
40524139Sjoerg
40624139Sjoerg    if (str[0] == '-')
40724139Sjoerg    {
40824139Sjoerg	/* explicit signal specified */
40924139Sjoerg	if ((nptr = next_field(str)) == NULL)
41024139Sjoerg	{
41124139Sjoerg	    return(" kill: no processes specified");
41224139Sjoerg	}
41324139Sjoerg
41424139Sjoerg	if (isdigit(str[1]))
41524139Sjoerg	{
41624139Sjoerg	    (void) scanint(str + 1, &signum);
41724139Sjoerg	    if (signum <= 0 || signum >= NSIG)
41824139Sjoerg	    {
41924139Sjoerg		return(" invalid signal number");
42024139Sjoerg	    }
42124139Sjoerg	}
42224139Sjoerg	else
42324139Sjoerg	{
42424139Sjoerg	    /* translate the name into a number */
42524139Sjoerg	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
42624139Sjoerg	    {
42724139Sjoerg		if (strcmp(sigp->name, str + 1) == 0)
42824139Sjoerg		{
42924139Sjoerg		    signum = sigp->number;
43024139Sjoerg		    break;
43124139Sjoerg		}
43224139Sjoerg	    }
43324139Sjoerg
43424139Sjoerg	    /* was it ever found */
43524139Sjoerg	    if (sigp->name == NULL)
43624139Sjoerg	    {
43724139Sjoerg		return(" bad signal name");
43824139Sjoerg	    }
43924139Sjoerg	}
44024139Sjoerg	/* put the new pointer in place */
44124139Sjoerg	str = nptr;
44224139Sjoerg    }
44324139Sjoerg
44424139Sjoerg    /* loop thru the string, killing processes */
44524139Sjoerg    do
44624139Sjoerg    {
44724139Sjoerg	if (scanint(str, &procnum) == -1)
44824139Sjoerg	{
44924139Sjoerg	    ERROR(str, 0);
45024139Sjoerg	}
45124139Sjoerg	else
45224139Sjoerg	{
45324139Sjoerg	    /* check process owner if we're not root */
45424139Sjoerg	    if (uid && (uid != proc_owner(procnum)))
45524139Sjoerg	    {
45624139Sjoerg		ERROR(str, EACCES);
45724139Sjoerg	    }
45824139Sjoerg	    /* go in for the kill */
45924139Sjoerg	    else if (kill(procnum, signum) == -1)
46024139Sjoerg	    {
46124139Sjoerg		/* chalk up an error */
46224139Sjoerg		ERROR(str, errno);
46324139Sjoerg	    }
46424139Sjoerg	}
46524139Sjoerg    } while ((str = next_field(str)) != NULL);
46624139Sjoerg
46724139Sjoerg    /* return appropriate error string */
46824139Sjoerg    return(err_string());
46924139Sjoerg}
47024139Sjoerg
47124139Sjoerg/*
47224139Sjoerg *  renice_procs(str) - change the "nice" of processes, much like the
47324139Sjoerg *		"renice" command does; invoked in response to 'r'.
47424139Sjoerg */
47524139Sjoerg
47624139Sjoergchar *renice_procs(str)
47724139Sjoerg
47824139Sjoergchar *str;
47924139Sjoerg
48024139Sjoerg{
48124139Sjoerg    register char negate;
48224139Sjoerg    int prio;
48324139Sjoerg    int procnum;
48424139Sjoerg    int uid;
48524139Sjoerg
48624139Sjoerg    ERR_RESET;
48724139Sjoerg    uid = getuid();
48824139Sjoerg
48924139Sjoerg    /* allow for negative priority values */
49024139Sjoerg    if ((negate = (*str == '-')) != 0)
49124139Sjoerg    {
49224139Sjoerg	/* move past the minus sign */
49324139Sjoerg	str++;
49424139Sjoerg    }
49524139Sjoerg
49624139Sjoerg    /* use procnum as a temporary holding place and get the number */
49724139Sjoerg    procnum = scanint(str, &prio);
49824139Sjoerg
49924139Sjoerg    /* negate if necessary */
50024139Sjoerg    if (negate)
50124139Sjoerg    {
50224139Sjoerg	prio = -prio;
50324139Sjoerg    }
50424139Sjoerg
50524139Sjoerg#if defined(PRIO_MIN) && defined(PRIO_MAX)
50624139Sjoerg    /* check for validity */
50724139Sjoerg    if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
50824139Sjoerg    {
50924139Sjoerg	return(" bad priority value");
51024139Sjoerg    }
51124139Sjoerg#endif
51224139Sjoerg
51324139Sjoerg    /* move to the first process number */
51424139Sjoerg    if ((str = next_field(str)) == NULL)
51524139Sjoerg    {
51624139Sjoerg	return(" no processes specified");
51724139Sjoerg    }
51824139Sjoerg
51924139Sjoerg    /* loop thru the process numbers, renicing each one */
52024139Sjoerg    do
52124139Sjoerg    {
52224139Sjoerg	if (scanint(str, &procnum) == -1)
52324139Sjoerg	{
52424139Sjoerg	    ERROR(str, 0);
52524139Sjoerg	}
52624139Sjoerg
52724139Sjoerg	/* check process owner if we're not root */
52824139Sjoerg	else if (uid && (uid != proc_owner(procnum)))
52924139Sjoerg	{
53024139Sjoerg	    ERROR(str, EACCES);
53124139Sjoerg	}
53224139Sjoerg	else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
53324139Sjoerg	{
53424139Sjoerg	    ERROR(str, errno);
53524139Sjoerg	}
53624139Sjoerg    } while ((str = next_field(str)) != NULL);
53724139Sjoerg
53824139Sjoerg    /* return appropriate error string */
53924139Sjoerg    return(err_string());
54024139Sjoerg}
54124139Sjoerg
542