home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / commands.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  10.1 KB  |  505 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.  
  98.     /* if there is nothing left of the string, return NULL */
  99.     /* This fix is dedicated to Greg Earle */
  100.     return(*str == '\0' ? NULL : str);
  101. }
  102.  
  103. scanint(str, intp)
  104.  
  105. char *str;
  106. int  *intp;
  107.  
  108. {
  109.     register int val = 0;
  110.     register char ch;
  111.  
  112.     /* if there is nothing left of the string, flag it as an error */
  113.     /* This fix is dedicated to Greg Earle */
  114.     if (*str == '\0')
  115.     {
  116.     return(-1);
  117.     }
  118.  
  119.     while ((ch = *str++) != '\0')
  120.     {
  121.     if (isdigit(ch))
  122.     {
  123.         val = val * 10 + (ch - '0');
  124.     }
  125.     else if (isspace(ch))
  126.     {
  127.         break;
  128.     }
  129.     else
  130.     {
  131.         return(-1);
  132.     }
  133.     }
  134.     *intp = val;
  135.     return(0);
  136. }
  137.  
  138. /*
  139.  *  Some of the commands make system calls that could generate errors.
  140.  *  These errors are collected up in an array of structures for later
  141.  *  contemplation and display.  Such routines return a string containing an
  142.  *  error message, or NULL if no errors occurred.  The next few routines are
  143.  *  for manipulating and displaying these errors.  We need an upper limit on
  144.  *  the number of errors, so we arbitrarily choose 20.
  145.  */
  146.  
  147. #define ERRMAX 20
  148.  
  149. struct errs        /* structure for a system-call error */
  150. {
  151.     int  errno;        /* value of errno (that is, the actual error) */
  152.     char *arg;        /* argument that caused the error */
  153. };
  154.  
  155. static struct errs errs[ERRMAX];
  156. static int errcnt;
  157. static char *err_toomany = " too many errors occurred";
  158. static char *err_listem = 
  159.     " Many errors occurred.  Press `e' to display the list of errors.";
  160.  
  161. /* These macros get used to reset and log the errors */
  162. #define ERR_RESET   errcnt = 0
  163. #define ERROR(p, e) if (errcnt >= ERRMAX) \
  164.             { \
  165.             return(err_toomany); \
  166.             } \
  167.             else \
  168.             { \
  169.             errs[errcnt].arg = (p); \
  170.             errs[errcnt++].errno = (e); \
  171.             }
  172.  
  173. /*
  174.  *  err_string() - return an appropriate error string.  This is what the
  175.  *    command will return for displaying.  If no errors were logged, then
  176.  *    return NULL.  The maximum length of the error string is defined by
  177.  *    "STRMAX".
  178.  */
  179.  
  180. #define STRMAX 80
  181.  
  182. char *err_string()
  183.  
  184. {
  185.     register struct errs *errp;
  186.     register int  cnt = 0;
  187.     register int  first = Yes;
  188.     register int  currerr = -1;
  189.     int stringlen;        /* characters still available in "string" */
  190.     static char string[STRMAX];
  191.  
  192.     /* if there are no errors, return NULL */
  193.     if (errcnt == 0)
  194.     {
  195.     return(NULL);
  196.     }
  197.  
  198.     /* sort the errors */
  199.     qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
  200.  
  201.     /* need a space at the front of the error string */
  202.     string[0] = ' ';
  203.     string[1] = '\0';
  204.     stringlen = STRMAX - 2;
  205.  
  206.     /* loop thru the sorted list, building an error string */
  207.     while (cnt < errcnt)
  208.     {
  209.     errp = &(errs[cnt++]);
  210.     if (errp->errno != currerr)
  211.     {
  212.         if (currerr != -1)
  213.         {
  214.         if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
  215.         {
  216.             return(err_listem);
  217.         }
  218.         (void) strcat(string, "; ");      /* we know there's more */
  219.         }
  220.         currerr = errp->errno;
  221.         first = Yes;
  222.     }
  223.     if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
  224.     {
  225.         return(err_listem);
  226.     }
  227.     first = No;
  228.     }
  229.  
  230.     /* add final message */
  231.     stringlen = str_adderr(string, stringlen, currerr);
  232.  
  233.     /* return the error string */
  234.     return(stringlen == 0 ? err_listem : string);
  235. }
  236.  
  237. /*
  238.  *  str_adderr(str, len, err) - add an explanation of error "err" to
  239.  *    the string "str".
  240.  */
  241.  
  242. str_adderr(str, len, err)
  243.  
  244. char *str;
  245. int len;
  246. int err;
  247.  
  248. {
  249.     register char *msg;
  250.     register int  msglen;
  251.  
  252.     msg = err == 0 ? "Not a number" : errmsg(err);
  253.     msglen = strlen(msg) + 2;
  254.     if (len <= msglen)
  255.     {
  256.     return(0);
  257.     }
  258.     (void) strcat(str, ": ");
  259.     (void) strcat(str, msg);
  260.     return(len - msglen);
  261. }
  262.  
  263. /*
  264.  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
  265.  *    the string "str".  This is the first in the group when "first"
  266.  *    is set (indicating that a comma should NOT be added to the front).
  267.  */
  268.  
  269. str_addarg(str, len, arg, first)
  270.  
  271. char *str;
  272. int  len;
  273. char *arg;
  274. int  first;
  275.  
  276. {
  277.     register int arglen;
  278.  
  279.     arglen = strlen(arg);
  280.     if (!first)
  281.     {
  282.     arglen += 2;
  283.     }
  284.     if (len <= arglen)
  285.     {
  286.     return(0);
  287.     }
  288.     if (!first)
  289.     {
  290.     (void) strcat(str, ", ");
  291.     }
  292.     (void) strcat(str, arg);
  293.     return(len - arglen);
  294. }
  295.  
  296. /*
  297.  *  err_compar(p1, p2) - comparison routine used by "qsort"
  298.  *    for sorting errors.
  299.  */
  300.  
  301. err_compar(p1, p2)
  302.  
  303. register struct errs *p1, *p2;
  304.  
  305. {
  306.     register int result;
  307.  
  308.     if ((result = p1->errno - p2->errno) == 0)
  309.     {
  310.     return(strcmp(p1->arg, p2->arg));
  311.     }
  312.     return(result);
  313. }
  314.  
  315. /*
  316.  *  error_count() - return the number of errors currently logged.
  317.  */
  318.  
  319. error_count()
  320.  
  321. {
  322.     return(errcnt);
  323. }
  324.  
  325. /*
  326.  *  show_errors() - display on stdout the current log of errors.
  327.  */
  328.  
  329. show_errors()
  330.  
  331. {
  332.     register int cnt = 0;
  333.     register struct errs *errp = errs;
  334.  
  335.     printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
  336.     while (cnt++ < errcnt)
  337.     {
  338.     printf("%5s: %s\n", errp->arg,
  339.         errp->errno == 0 ? "Not a number" : errmsg(errp->errno));
  340.     errp++;
  341.     }
  342. }
  343.  
  344. /*
  345.  *  kill_procs(str) - send signals to processes, much like the "kill"
  346.  *        command does; invoked in response to 'k'.
  347.  */
  348.  
  349. char *kill_procs(str)
  350.  
  351. char *str;
  352.  
  353. {
  354.     register char *nptr;
  355.     int signum = SIGTERM;    /* default */
  356.     int procnum;
  357.     struct sigdesc *sigp;
  358.     int uid;
  359.  
  360.     /* reset error array */
  361.     ERR_RESET;
  362.  
  363.     /* remember our uid */
  364.     uid = getuid();
  365.  
  366.     /* skip over leading white space */
  367.     while (isspace(*str)) str++;
  368.  
  369.     if (str[0] == '-')
  370.     {
  371.     /* explicit signal specified */
  372.     if ((nptr = next_field(str)) == NULL)
  373.     {
  374.         return(" kill: no processes specified");
  375.     }
  376.  
  377.     if (isdigit(str[1]))
  378.     {
  379.         (void) scanint(str + 1, &signum);
  380.         if (signum <= 0 || signum >= NSIG)
  381.         {
  382.         return(" invalid signal number");
  383.         }
  384.     }
  385.     else 
  386.     {
  387.         /* translate the name into a number */
  388.         for (sigp = sigdesc; sigp->name != NULL; sigp++)
  389.         {
  390.         if (strcmp(sigp->name, str + 1) == 0)
  391.         {
  392.             signum = sigp->number;
  393.             break;
  394.         }
  395.         }
  396.  
  397.         /* was it ever found */
  398.         if (sigp->name == NULL)
  399.         {
  400.         return(" bad signal name");
  401.         }
  402.     }
  403.     /* put the new pointer in place */
  404.     str = nptr;
  405.     }
  406.  
  407.     /* loop thru the string, killing processes */
  408.     do
  409.     {
  410.     if (scanint(str, &procnum) == -1)
  411.     {
  412.         ERROR(str, 0);
  413.     }
  414.     else
  415.     {
  416.         /* check process owner if we're not root */
  417.         if (uid && (uid != proc_owner(procnum)))
  418.         {
  419.         ERROR(str, EACCES);
  420.         }
  421.         /* go in for the kill */
  422.         else if (kill(procnum, signum) == -1)
  423.         {
  424.         /* chalk up an error */
  425.         ERROR(str, errno);
  426.         }
  427.     }
  428.     } while ((str = next_field(str)) != NULL);
  429.  
  430.     /* return appropriate error string */
  431.     return(err_string());
  432. }
  433.  
  434. /*
  435.  *  renice_procs(str) - change the "nice" of processes, much like the
  436.  *        "renice" command does; invoked in response to 'r'.
  437.  */
  438.  
  439. char *renice_procs(str)
  440.  
  441. char *str;
  442.  
  443. {
  444.     register char negate;
  445.     int prio;
  446.     int procnum;
  447.     int uid;
  448.  
  449.     ERR_RESET;
  450.     uid = getuid();
  451.  
  452.     /* allow for negative priority values */
  453.     if ((negate = (*str == '-')) != 0)
  454.     {
  455.     /* move past the minus sign */
  456.     str++;
  457.     }
  458.  
  459.     /* use procnum as a temporary holding place and get the number */
  460.     procnum = scanint(str, &prio);
  461.  
  462.     /* negate if necessary */
  463.     if (negate)
  464.     {
  465.     prio = -prio;
  466.     }
  467.  
  468. #if defined(PRIO_MIN) && defined(PRIO_MAX)
  469.     /* check for validity */
  470.     if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
  471.     {
  472.     return(" bad priority value");
  473.     }
  474. #endif
  475.  
  476.     /* move to the first process number */
  477.     if ((str = next_field(str)) == NULL)
  478.     {
  479.     return(" no processes specified");
  480.     }
  481.  
  482.     /* loop thru the process numbers, renicing each one */
  483.     do
  484.     {
  485.     if (scanint(str, &procnum) == -1)
  486.     {
  487.         ERROR(str, 0);
  488.     }
  489.  
  490.     /* check process owner if we're not root */
  491.     else if (uid && (uid != proc_owner(procnum)))
  492.     {
  493.         ERROR(str, EACCES);
  494.     }
  495.     else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
  496.     {
  497.         ERROR(str, errno);
  498.     }
  499.     } while ((str = next_field(str)) != NULL);
  500.  
  501.     /* return appropriate error string */
  502.     return(err_string());
  503. }
  504.  
  505.