home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume27 / top-3.2 / part02 / commands.c next >
Encoding:
C/C++ Source or Header  |  1993-08-08  |  9.7 KB  |  492 lines

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