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

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