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