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