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