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