source: trunk/third/top/commands.c @ 9084

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