home *** CD-ROM | disk | FTP | other *** search
/ ftp.muug.mb.ca / 2014.06.ftp.muug.mb.ca.tar / ftp.muug.mb.ca / pub / src / top / commands.c < prev    next >
C/C++ Source or Header  |  1992-02-01  |  9KB  |  456 lines

  1. /*
  2.  *  Top users display for Berkeley Unix
  3.  *
  4.  *  This file contains the routines that implement some of the interactive
  5.  *  mode commands.  Note that some of the commands are implemented in-line
  6.  *  in "main".  This is necessary because they change the global state of
  7.  *  "top" (i.e.:  changing the number of processes to display).
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <signal.h>
  13. #include <strings.h>
  14. #include <sys/time.h>
  15. #include <sys/resource.h>
  16. #include "sigdesc.h"        /* generated automatically */
  17. #include "boolean.h"
  18.  
  19. extern int  errno;
  20. extern int  sys_nerr;
  21. extern char *sys_errlist[];
  22.  
  23. extern char *copyright;
  24.  
  25. /* imported from screen.c */
  26. extern int overstrike;
  27.  
  28. int err_compar();
  29. char *err_string();
  30. char *index();
  31.  
  32. /*
  33.  *  show_help() - display the help screen; invoked in response to
  34.  *        either 'h' or '?'.
  35.  */
  36.  
  37. show_help()
  38.  
  39. {
  40.     fputs(copyright, stdout);
  41.     fputs("\n\n\
  42. A top users display for Unix\n\
  43. \n\
  44. These single-character commands are available:\n\
  45. \n\
  46. ^L      - redraw screen\n\
  47. q       - quit\n\
  48. h or ?  - help; show this text\n", stdout);
  49.  
  50.     /* not all commands are availalbe with overstrike terminals */
  51.     if (overstrike)
  52.     {
  53.     fputs("\n\
  54. Other commands are also available, but this terminal is not\n\
  55. sophisticated enough to handle those commands gracefully.\n\n", stdout);
  56.     }
  57.     else
  58.     {
  59.     fputs("\
  60. d       - change number of displays to show\n\
  61. e       - list errors generated by last \"kill\" or \"renice\" command\n\
  62. k       - kill processes; send a signal to a list of processes\n\
  63. n or #  - change number of processes to display\n\
  64. r       - renice a process\n\
  65. s       - change number of seconds to delay between updates\n\
  66. \n\
  67. \n", stdout);
  68.     }
  69. }
  70.  
  71. /*
  72.  *  Utility routines that help with some of the commands.
  73.  */
  74.  
  75. char *next_field(str)
  76.  
  77. register char *str;
  78.  
  79. {
  80.     if ((str = index(str, ' ')) == NULL)
  81.     {
  82.     return(NULL);
  83.     }
  84.     *str = '\0';
  85.     while (*++str == ' ') /* loop */;
  86.     return(str);
  87. }
  88.  
  89. scanint(str, intp)
  90.  
  91. char *str;
  92. int  *intp;
  93.  
  94. {
  95.     register int val = 0;
  96.     register char ch;
  97.  
  98.     while ((ch = *str++) != '\0')
  99.     {
  100.     if (isdigit(ch))
  101.     {
  102.         val = val * 10 + (ch - '0');
  103.     }
  104.     else if (isspace(ch))
  105.     {
  106.         break;
  107.     }
  108.     else
  109.     {
  110.         return(-1);
  111.     }
  112.     }
  113.     *intp = val;
  114.     return(0);
  115. }
  116.  
  117. /*
  118.  *  Some of the commands make system calls that could generate errors.
  119.  *  These errors are collected up in an array of structures for later
  120.  *  contemplation and display.  Such routines return a string containing an
  121.  *  error message, or NULL if no errors occurred.  The next few routines are
  122.  *  for manipulating and displaying these errors.  We need an upper limit on
  123.  *  the number of errors, so we arbitrarily choose 20.
  124.  */
  125.  
  126. #define ERRMAX 20
  127.  
  128. struct errs        /* structure for a system-call error */
  129. {
  130.     int  errno;        /* value of errno (that is, the actual error) */
  131.     char *arg;        /* argument that caused the error */
  132. };
  133.  
  134. static struct errs errs[ERRMAX];
  135. static int errcnt;
  136. static char *err_toomany = " too many errors occurred";
  137. static char *err_listem = 
  138.     " Many errors occurred.  Press `e' to display the list of errors.";
  139.  
  140. /* These macros get used to reset and log the errors */
  141. #define ERR_RESET   errcnt = 0
  142. #define ERROR(p, e) if (errcnt >= ERRMAX) \
  143.             { \
  144.             return(err_toomany); \
  145.             } \
  146.             else \
  147.             { \
  148.             errs[errcnt].arg = (p); \
  149.             errs[errcnt++].errno = (e); \
  150.             }
  151.  
  152. /*
  153.  *  err_string() - return an appropriate error string.  This is what the
  154.  *    command will return for displaying.  If no errors were logged, then
  155.  *    return NULL.  The maximum length of the error string is defined by
  156.  *    "STRMAX".
  157.  */
  158.  
  159. #define STRMAX 80
  160.  
  161. char *err_string()
  162.  
  163. {
  164.     register struct errs *errp;
  165.     register int  cnt = 0;
  166.     register int  first = Yes;
  167.     register int  currerr = -1;
  168.     int stringlen;        /* characters still available in "string" */
  169.     static char string[STRMAX];
  170.  
  171.     /* if there are no errors, return NULL */
  172.     if (errcnt == 0)
  173.     {
  174.     return(NULL);
  175.     }
  176.  
  177.     /* sort the errors */
  178.     qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
  179.  
  180.     /* need a space at the front of the error string */
  181.     string[0] = ' ';
  182.     string[1] = '\0';
  183.     stringlen = STRMAX - 2;
  184.  
  185.     /* loop thru the sorted list, building an error string */
  186.     while (cnt < errcnt)
  187.     {
  188.     errp = &(errs[cnt++]);
  189.     if (errp->errno != currerr)
  190.     {
  191.         if (currerr != -1)
  192.         {
  193.         if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
  194.         {
  195.             return(err_listem);
  196.         }
  197.         (void) strcat(string, "; ");      /* we know there's more */
  198.         }
  199.         currerr = errp->errno;
  200.         first = Yes;
  201.     }
  202.     if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
  203.     {
  204.         return(err_listem);
  205.     }
  206.     first = No;
  207.     }
  208.  
  209.     /* add final message */
  210.     stringlen = str_adderr(string, stringlen, currerr);
  211.  
  212.     /* return the error string */
  213.     return(stringlen == 0 ? err_listem : string);
  214. }
  215.  
  216. /*
  217.  *  str_adderr(str, len, err) - add an explanation of error "err" to
  218.  *    the string "str".
  219.  */
  220.  
  221. str_adderr(str, len, err)
  222.  
  223. char *str;
  224. int len;
  225. int err;
  226.  
  227. {
  228.     register char *msg;
  229.     register int  msglen;
  230.  
  231.     msg = err == 0 ? "Not a number" : sys_errlist[err];
  232.     msglen = strlen(msg) + 2;
  233.     if (len <= msglen)
  234.     {
  235.     return(0);
  236.     }
  237.     (void) strcat(str, ": ");
  238.     (void) strcat(str, msg);
  239.     return(len - msglen);
  240. }
  241.  
  242. /*
  243.  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
  244.  *    the string "str".  This is the first in the group when "first"
  245.  *    is set (indicating that a comma should NOT be added to the front).
  246.  */
  247.  
  248. str_addarg(str, len, arg, first)
  249.  
  250. char *str;
  251. int  len;
  252. char *arg;
  253. int  first;
  254.  
  255. {
  256.     register int arglen;
  257.  
  258.     arglen = strlen(arg);
  259.     if (!first)
  260.     {
  261.     arglen += 2;
  262.     }
  263.     if (len <= arglen)
  264.     {
  265.     return(0);
  266.     }
  267.     if (!first)
  268.     {
  269.     (void) strcat(str, ", ");
  270.     }
  271.     (void) strcat(str, arg);
  272.     return(len - arglen);
  273. }
  274.  
  275. /*
  276.  *  err_compar(p1, p2) - comparison routine used by "qsort"
  277.  *    for sorting errors.
  278.  */
  279.  
  280. err_compar(p1, p2)
  281.  
  282. register struct errs *p1, *p2;
  283.  
  284. {
  285.     register int result;
  286.  
  287.     if ((result = p1->errno - p2->errno) == 0)
  288.     {
  289.     return(strcmp(p1->arg, p2->arg));
  290.     }
  291.     return(result);
  292. }
  293.  
  294. /*
  295.  *  error_count() - return the number of errors currently logged.
  296.  */
  297.  
  298. error_count()
  299.  
  300. {
  301.     return(errcnt);
  302. }
  303.  
  304. /*
  305.  *  show_errors() - display on stdout the current log of errors.
  306.  */
  307.  
  308. show_errors()
  309.  
  310. {
  311.     register int cnt = 0;
  312.     register struct errs *errp = errs;
  313.  
  314.     printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
  315.     while (cnt++ < errcnt)
  316.     {
  317.     printf("%5s: %s\n", errp->arg,
  318.         errp->errno == 0 ? "Not a number" : sys_errlist[errp->errno]);
  319.     errp++;
  320.     }
  321. }
  322.  
  323. /*
  324.  *  kill_procs(str) - send signals to processes, much like the "kill"
  325.  *        command does; invoked in response to 'k'.
  326.  */
  327.  
  328. char *kill_procs(str)
  329.  
  330. char *str;
  331.  
  332. {
  333.     register char *nptr;
  334.     int signum = SIGTERM;    /* default */
  335.     int procnum;
  336.     struct sigdesc *sigp;
  337.  
  338.     ERR_RESET;
  339.     if (str[0] == '-')
  340.     {
  341.     /* explicit signal specified */
  342.     if ((nptr = next_field(str)) == NULL)
  343.     {
  344.         return(" kill: no processes specified");
  345.     }
  346.  
  347.     if (isdigit(str[1]))
  348.     {
  349.         (void) scanint(str + 1, &signum);
  350.         if (signum <= 0 || signum >= NSIG)
  351.         {
  352.         return(" invalid signal number");
  353.         }
  354.     }
  355.     else 
  356.     {
  357.         /* translate the name into a number */
  358.         for (sigp = sigdesc; sigp->name != NULL; sigp++)
  359.         {
  360.         if (strcmp(sigp->name, str + 1) == 0)
  361.         {
  362.             signum = sigp->number;
  363.             break;
  364.         }
  365.         }
  366.  
  367.         /* was it ever found */
  368.         if (sigp->name == NULL)
  369.         {
  370.         return(" bad signal name");
  371.         }
  372.     }
  373.     /* put the new pointer in place */
  374.     str = nptr;
  375.     }
  376.  
  377.     /* loop thru the string, killing processes */
  378.     do
  379.     {
  380.     if (scanint(str, &procnum) == -1)
  381.     {
  382.         ERROR(str, 0);
  383.     }
  384.     else if (kill(procnum, signum) == -1)
  385.     {
  386.         /* chalk up an error */
  387.         ERROR(str, errno);
  388.     }
  389.     } while ((str = next_field(str)) != NULL);
  390.  
  391.     /* return appropriate error string */
  392.     return(err_string());
  393. }
  394.  
  395. /*
  396.  *  renice_procs(str) - change the "nice" of processes, much like the
  397.  *        "renice" command does; invoked in response to 'r'.
  398.  */
  399.  
  400. char *renice_procs(str)
  401.  
  402. char *str;
  403.  
  404. {
  405.     register char negate;
  406.     int prio;
  407.     int procnum;
  408.  
  409.     ERR_RESET;
  410.  
  411.     /* allow for negative priority values */
  412.     if ((negate = *str == '-'))
  413.     {
  414.     /* move past the minus sign */
  415.     str++;
  416.     }
  417.  
  418.     /* use procnum as a temporary holding place and get the number */
  419.     procnum = scanint(str, &prio);
  420.  
  421.     /* negate if necessary */
  422.     if (negate)
  423.     {
  424.     prio = -prio;
  425.     }
  426.  
  427.     /* check for validity */
  428.     if (procnum == -1 || prio <= PRIO_MIN || prio >= PRIO_MAX)
  429.     {
  430.     return(" bad priority value");
  431.     }
  432.  
  433.     /* move to the first process number */
  434.     if ((str = next_field(str)) == NULL)
  435.     {
  436.     return(" no processes specified");
  437.     }
  438.  
  439.     /* loop thru the process numbers, renicing each one */
  440.     do
  441.     {
  442.     if (scanint(str, &procnum) == -1)
  443.     {
  444.         ERROR(str, 0);
  445.     }
  446.     else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
  447.     {
  448.         ERROR(str, errno);
  449.     }
  450.     } while ((str = next_field(str)) != NULL);
  451.  
  452.     /* return appropriate error string */
  453.     return(err_string());
  454. }
  455.  
  456.