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: stable/11/contrib/top/commands.c 307757 2016-10-22 00:35:40Z des $
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"
22300395Sngie
2324139Sjoerg#include <sys/time.h>
2424139Sjoerg#include <sys/resource.h>
2524139Sjoerg
26300395Sngie#include <ctype.h>
27300395Sngie#include <errno.h>
28300395Sngie#include <signal.h>
29300395Sngie#include <unistd.h>
30300395Sngie
31300395Sngie#include "commands.h"
3224139Sjoerg#include "sigdesc.h"		/* generated automatically */
3337451Sbde#include "top.h"
3424139Sjoerg#include "boolean.h"
3524139Sjoerg#include "utils.h"
36300395Sngie#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();
47300395Sngiestatic int str_adderr(char *str, int len, int err);
48300395Sngiestatic 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
55300395Sngievoid
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\
85265251SbdreweryJ       - 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\
107307757Sdesw       - toggle the display of swap use for each process\n\
108222530Sjhbz       - toggle the displaying of the system idle process\n\
10924139Sjoerg\n\
11024139Sjoerg\n", stdout);
11124139Sjoerg    }
11224139Sjoerg}
11324139Sjoerg
11424139Sjoerg/*
11524139Sjoerg *  Utility routines that help with some of the commands.
11624139Sjoerg */
11724139Sjoerg
11824139Sjoergchar *next_field(str)
11924139Sjoerg
12024139Sjoergregister char *str;
12124139Sjoerg
12224139Sjoerg{
12324139Sjoerg    if ((str = strchr(str, ' ')) == NULL)
12424139Sjoerg    {
12524139Sjoerg	return(NULL);
12624139Sjoerg    }
12724139Sjoerg    *str = '\0';
12824139Sjoerg    while (*++str == ' ') /* loop */;
12924139Sjoerg
13024139Sjoerg    /* if there is nothing left of the string, return NULL */
13124139Sjoerg    /* This fix is dedicated to Greg Earle */
13224139Sjoerg    return(*str == '\0' ? NULL : str);
13324139Sjoerg}
13424139Sjoerg
135300395Sngieint
13624139Sjoergscanint(str, intp)
13724139Sjoerg
13824139Sjoergchar *str;
13924139Sjoergint  *intp;
14024139Sjoerg
14124139Sjoerg{
14224139Sjoerg    register int val = 0;
14324139Sjoerg    register char ch;
14424139Sjoerg
14524139Sjoerg    /* if there is nothing left of the string, flag it as an error */
14624139Sjoerg    /* This fix is dedicated to Greg Earle */
14724139Sjoerg    if (*str == '\0')
14824139Sjoerg    {
14924139Sjoerg	return(-1);
15024139Sjoerg    }
15124139Sjoerg
15224139Sjoerg    while ((ch = *str++) != '\0')
15324139Sjoerg    {
15424139Sjoerg	if (isdigit(ch))
15524139Sjoerg	{
15624139Sjoerg	    val = val * 10 + (ch - '0');
15724139Sjoerg	}
15824139Sjoerg	else if (isspace(ch))
15924139Sjoerg	{
16024139Sjoerg	    break;
16124139Sjoerg	}
16224139Sjoerg	else
16324139Sjoerg	{
16424139Sjoerg	    return(-1);
16524139Sjoerg	}
16624139Sjoerg    }
16724139Sjoerg    *intp = val;
16824139Sjoerg    return(0);
16924139Sjoerg}
17024139Sjoerg
17124139Sjoerg/*
17224139Sjoerg *  Some of the commands make system calls that could generate errors.
17324139Sjoerg *  These errors are collected up in an array of structures for later
17424139Sjoerg *  contemplation and display.  Such routines return a string containing an
17524139Sjoerg *  error message, or NULL if no errors occurred.  The next few routines are
17624139Sjoerg *  for manipulating and displaying these errors.  We need an upper limit on
17724139Sjoerg *  the number of errors, so we arbitrarily choose 20.
17824139Sjoerg */
17924139Sjoerg
18024139Sjoerg#define ERRMAX 20
18124139Sjoerg
18224139Sjoergstruct errs		/* structure for a system-call error */
18324139Sjoerg{
18489755Sdwmalone    int  errnum;	/* value of errno (that is, the actual error) */
18524139Sjoerg    char *arg;		/* argument that caused the error */
18624139Sjoerg};
18724139Sjoerg
18824139Sjoergstatic struct errs errs[ERRMAX];
18924139Sjoergstatic int errcnt;
19024139Sjoergstatic char *err_toomany = " too many errors occurred";
19124139Sjoergstatic char *err_listem =
19224139Sjoerg	" Many errors occurred.  Press `e' to display the list of errors.";
19324139Sjoerg
19424139Sjoerg/* These macros get used to reset and log the errors */
19524139Sjoerg#define ERR_RESET   errcnt = 0
19624139Sjoerg#define ERROR(p, e) if (errcnt >= ERRMAX) \
19724139Sjoerg		    { \
19824139Sjoerg			return(err_toomany); \
19924139Sjoerg		    } \
20024139Sjoerg		    else \
20124139Sjoerg		    { \
20224139Sjoerg			errs[errcnt].arg = (p); \
20389755Sdwmalone			errs[errcnt++].errnum = (e); \
20424139Sjoerg		    }
20524139Sjoerg
20624139Sjoerg/*
20724139Sjoerg *  err_string() - return an appropriate error string.  This is what the
20824139Sjoerg *	command will return for displaying.  If no errors were logged, then
20924139Sjoerg *	return NULL.  The maximum length of the error string is defined by
21024139Sjoerg *	"STRMAX".
21124139Sjoerg */
21224139Sjoerg
21324139Sjoerg#define STRMAX 80
21424139Sjoerg
21524139Sjoergchar *err_string()
21624139Sjoerg
21724139Sjoerg{
21824139Sjoerg    register struct errs *errp;
21924139Sjoerg    register int  cnt = 0;
22024139Sjoerg    register int  first = Yes;
22124139Sjoerg    register int  currerr = -1;
22224139Sjoerg    int stringlen;		/* characters still available in "string" */
22324139Sjoerg    static char string[STRMAX];
22424139Sjoerg
22524139Sjoerg    /* if there are no errors, return NULL */
22624139Sjoerg    if (errcnt == 0)
22724139Sjoerg    {
22824139Sjoerg	return(NULL);
22924139Sjoerg    }
23024139Sjoerg
23124139Sjoerg    /* sort the errors */
23224139Sjoerg    qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
23324139Sjoerg
23424139Sjoerg    /* need a space at the front of the error string */
23524139Sjoerg    string[0] = ' ';
23624139Sjoerg    string[1] = '\0';
23724139Sjoerg    stringlen = STRMAX - 2;
23824139Sjoerg
23924139Sjoerg    /* loop thru the sorted list, building an error string */
24024139Sjoerg    while (cnt < errcnt)
24124139Sjoerg    {
24224139Sjoerg	errp = &(errs[cnt++]);
24389755Sdwmalone	if (errp->errnum != currerr)
24424139Sjoerg	{
24524139Sjoerg	    if (currerr != -1)
24624139Sjoerg	    {
24724139Sjoerg		if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
24824139Sjoerg		{
24924139Sjoerg		    return(err_listem);
25024139Sjoerg		}
25124139Sjoerg		(void) strcat(string, "; ");	  /* we know there's more */
25224139Sjoerg	    }
25389755Sdwmalone	    currerr = errp->errnum;
25424139Sjoerg	    first = Yes;
25524139Sjoerg	}
25624139Sjoerg	if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
25724139Sjoerg	{
25824139Sjoerg	    return(err_listem);
25924139Sjoerg	}
26024139Sjoerg	first = No;
26124139Sjoerg    }
26224139Sjoerg
26324139Sjoerg    /* add final message */
26424139Sjoerg    stringlen = str_adderr(string, stringlen, currerr);
26524139Sjoerg
26624139Sjoerg    /* return the error string */
26724139Sjoerg    return(stringlen == 0 ? err_listem : string);
26824139Sjoerg}
26924139Sjoerg
27024139Sjoerg/*
27124139Sjoerg *  str_adderr(str, len, err) - add an explanation of error "err" to
27224139Sjoerg *	the string "str".
27324139Sjoerg */
27424139Sjoerg
275300395Sngiestatic int
27624139Sjoergstr_adderr(str, len, err)
27724139Sjoerg
27824139Sjoergchar *str;
27924139Sjoergint len;
28024139Sjoergint err;
28124139Sjoerg
28224139Sjoerg{
28324139Sjoerg    register char *msg;
28424139Sjoerg    register int  msglen;
28524139Sjoerg
28624139Sjoerg    msg = err == 0 ? "Not a number" : errmsg(err);
28724139Sjoerg    msglen = strlen(msg) + 2;
28824139Sjoerg    if (len <= msglen)
28924139Sjoerg    {
29024139Sjoerg	return(0);
29124139Sjoerg    }
29224139Sjoerg    (void) strcat(str, ": ");
29324139Sjoerg    (void) strcat(str, msg);
29424139Sjoerg    return(len - msglen);
29524139Sjoerg}
29624139Sjoerg
29724139Sjoerg/*
29824139Sjoerg *  str_addarg(str, len, arg, first) - add the string argument "arg" to
29924139Sjoerg *	the string "str".  This is the first in the group when "first"
30024139Sjoerg *	is set (indicating that a comma should NOT be added to the front).
30124139Sjoerg */
30224139Sjoerg
303300395Sngiestatic int
30424139Sjoergstr_addarg(str, len, arg, first)
30524139Sjoerg
30624139Sjoergchar *str;
30724139Sjoergint  len;
30824139Sjoergchar *arg;
30924139Sjoergint  first;
31024139Sjoerg
31124139Sjoerg{
31224139Sjoerg    register int arglen;
31324139Sjoerg
31424139Sjoerg    arglen = strlen(arg);
31524139Sjoerg    if (!first)
31624139Sjoerg    {
31724139Sjoerg	arglen += 2;
31824139Sjoerg    }
31924139Sjoerg    if (len <= arglen)
32024139Sjoerg    {
32124139Sjoerg	return(0);
32224139Sjoerg    }
32324139Sjoerg    if (!first)
32424139Sjoerg    {
32524139Sjoerg	(void) strcat(str, ", ");
32624139Sjoerg    }
32724139Sjoerg    (void) strcat(str, arg);
32824139Sjoerg    return(len - arglen);
32924139Sjoerg}
33024139Sjoerg
33124139Sjoerg/*
33224139Sjoerg *  err_compar(p1, p2) - comparison routine used by "qsort"
33324139Sjoerg *	for sorting errors.
33424139Sjoerg */
33524139Sjoerg
336300395Sngieint
33724139Sjoergerr_compar(p1, p2)
33824139Sjoerg
33924139Sjoergregister struct errs *p1, *p2;
34024139Sjoerg
34124139Sjoerg{
34224139Sjoerg    register int result;
34324139Sjoerg
34489755Sdwmalone    if ((result = p1->errnum - p2->errnum) == 0)
34524139Sjoerg    {
34624139Sjoerg	return(strcmp(p1->arg, p2->arg));
34724139Sjoerg    }
34824139Sjoerg    return(result);
34924139Sjoerg}
35024139Sjoerg
35124139Sjoerg/*
35224139Sjoerg *  error_count() - return the number of errors currently logged.
35324139Sjoerg */
35424139Sjoerg
355300395Sngieint
35624139Sjoergerror_count()
35724139Sjoerg
35824139Sjoerg{
35924139Sjoerg    return(errcnt);
36024139Sjoerg}
36124139Sjoerg
36224139Sjoerg/*
36324139Sjoerg *  show_errors() - display on stdout the current log of errors.
36424139Sjoerg */
36524139Sjoerg
366300395Sngievoid
36724139Sjoergshow_errors()
36824139Sjoerg
36924139Sjoerg{
37024139Sjoerg    register int cnt = 0;
37124139Sjoerg    register struct errs *errp = errs;
37224139Sjoerg
37324139Sjoerg    printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
37424139Sjoerg    while (cnt++ < errcnt)
37524139Sjoerg    {
37624139Sjoerg	printf("%5s: %s\n", errp->arg,
37789755Sdwmalone	    errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum));
37824139Sjoerg	errp++;
37924139Sjoerg    }
38024139Sjoerg}
38124139Sjoerg
38224139Sjoerg/*
38324139Sjoerg *  kill_procs(str) - send signals to processes, much like the "kill"
38424139Sjoerg *		command does; invoked in response to 'k'.
38524139Sjoerg */
38624139Sjoerg
38724139Sjoergchar *kill_procs(str)
38824139Sjoerg
38924139Sjoergchar *str;
39024139Sjoerg
39124139Sjoerg{
39224139Sjoerg    register char *nptr;
39324139Sjoerg    int signum = SIGTERM;	/* default */
39424139Sjoerg    int procnum;
39524139Sjoerg    struct sigdesc *sigp;
39624139Sjoerg    int uid;
39724139Sjoerg
39824139Sjoerg    /* reset error array */
39924139Sjoerg    ERR_RESET;
40024139Sjoerg
40124139Sjoerg    /* remember our uid */
40224139Sjoerg    uid = getuid();
40324139Sjoerg
40424139Sjoerg    /* skip over leading white space */
40524139Sjoerg    while (isspace(*str)) str++;
40624139Sjoerg
40724139Sjoerg    if (str[0] == '-')
40824139Sjoerg    {
40924139Sjoerg	/* explicit signal specified */
41024139Sjoerg	if ((nptr = next_field(str)) == NULL)
41124139Sjoerg	{
41224139Sjoerg	    return(" kill: no processes specified");
41324139Sjoerg	}
41424139Sjoerg
41524139Sjoerg	if (isdigit(str[1]))
41624139Sjoerg	{
41724139Sjoerg	    (void) scanint(str + 1, &signum);
41824139Sjoerg	    if (signum <= 0 || signum >= NSIG)
41924139Sjoerg	    {
42024139Sjoerg		return(" invalid signal number");
42124139Sjoerg	    }
42224139Sjoerg	}
42324139Sjoerg	else
42424139Sjoerg	{
42524139Sjoerg	    /* translate the name into a number */
42624139Sjoerg	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
42724139Sjoerg	    {
42824139Sjoerg		if (strcmp(sigp->name, str + 1) == 0)
42924139Sjoerg		{
43024139Sjoerg		    signum = sigp->number;
43124139Sjoerg		    break;
43224139Sjoerg		}
43324139Sjoerg	    }
43424139Sjoerg
43524139Sjoerg	    /* was it ever found */
43624139Sjoerg	    if (sigp->name == NULL)
43724139Sjoerg	    {
43824139Sjoerg		return(" bad signal name");
43924139Sjoerg	    }
44024139Sjoerg	}
44124139Sjoerg	/* put the new pointer in place */
44224139Sjoerg	str = nptr;
44324139Sjoerg    }
44424139Sjoerg
44524139Sjoerg    /* loop thru the string, killing processes */
44624139Sjoerg    do
44724139Sjoerg    {
44824139Sjoerg	if (scanint(str, &procnum) == -1)
44924139Sjoerg	{
45024139Sjoerg	    ERROR(str, 0);
45124139Sjoerg	}
45224139Sjoerg	else
45324139Sjoerg	{
45424139Sjoerg	    /* check process owner if we're not root */
45524139Sjoerg	    if (uid && (uid != proc_owner(procnum)))
45624139Sjoerg	    {
45724139Sjoerg		ERROR(str, EACCES);
45824139Sjoerg	    }
45924139Sjoerg	    /* go in for the kill */
46024139Sjoerg	    else if (kill(procnum, signum) == -1)
46124139Sjoerg	    {
46224139Sjoerg		/* chalk up an error */
46324139Sjoerg		ERROR(str, errno);
46424139Sjoerg	    }
46524139Sjoerg	}
46624139Sjoerg    } while ((str = next_field(str)) != NULL);
46724139Sjoerg
46824139Sjoerg    /* return appropriate error string */
46924139Sjoerg    return(err_string());
47024139Sjoerg}
47124139Sjoerg
47224139Sjoerg/*
47324139Sjoerg *  renice_procs(str) - change the "nice" of processes, much like the
47424139Sjoerg *		"renice" command does; invoked in response to 'r'.
47524139Sjoerg */
47624139Sjoerg
47724139Sjoergchar *renice_procs(str)
47824139Sjoerg
47924139Sjoergchar *str;
48024139Sjoerg
48124139Sjoerg{
48224139Sjoerg    register char negate;
48324139Sjoerg    int prio;
48424139Sjoerg    int procnum;
48524139Sjoerg    int uid;
48624139Sjoerg
48724139Sjoerg    ERR_RESET;
48824139Sjoerg    uid = getuid();
48924139Sjoerg
49024139Sjoerg    /* allow for negative priority values */
49124139Sjoerg    if ((negate = (*str == '-')) != 0)
49224139Sjoerg    {
49324139Sjoerg	/* move past the minus sign */
49424139Sjoerg	str++;
49524139Sjoerg    }
49624139Sjoerg
49724139Sjoerg    /* use procnum as a temporary holding place and get the number */
49824139Sjoerg    procnum = scanint(str, &prio);
49924139Sjoerg
50024139Sjoerg    /* negate if necessary */
50124139Sjoerg    if (negate)
50224139Sjoerg    {
50324139Sjoerg	prio = -prio;
50424139Sjoerg    }
50524139Sjoerg
50624139Sjoerg#if defined(PRIO_MIN) && defined(PRIO_MAX)
50724139Sjoerg    /* check for validity */
50824139Sjoerg    if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
50924139Sjoerg    {
51024139Sjoerg	return(" bad priority value");
51124139Sjoerg    }
51224139Sjoerg#endif
51324139Sjoerg
51424139Sjoerg    /* move to the first process number */
51524139Sjoerg    if ((str = next_field(str)) == NULL)
51624139Sjoerg    {
51724139Sjoerg	return(" no processes specified");
51824139Sjoerg    }
51924139Sjoerg
52024139Sjoerg    /* loop thru the process numbers, renicing each one */
52124139Sjoerg    do
52224139Sjoerg    {
52324139Sjoerg	if (scanint(str, &procnum) == -1)
52424139Sjoerg	{
52524139Sjoerg	    ERROR(str, 0);
52624139Sjoerg	}
52724139Sjoerg
52824139Sjoerg	/* check process owner if we're not root */
52924139Sjoerg	else if (uid && (uid != proc_owner(procnum)))
53024139Sjoerg	{
53124139Sjoerg	    ERROR(str, EACCES);
53224139Sjoerg	}
53324139Sjoerg	else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
53424139Sjoerg	{
53524139Sjoerg	    ERROR(str, errno);
53624139Sjoerg	}
53724139Sjoerg    } while ((str = next_field(str)) != NULL);
53824139Sjoerg
53924139Sjoerg    /* return appropriate error string */
54024139Sjoerg    return(err_string());
54124139Sjoerg}
54224139Sjoerg
543