]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/top/commands.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / usr.bin / top / commands.c
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  * $FreeBSD$
12  */
13
14 /*
15  *  This file contains the routines that implement some of the interactive
16  *  mode commands.  Note that some of the commands are implemented in-line
17  *  in "main".  This is necessary because they change the global state of
18  *  "top" (i.e.:  changing the number of processes to display).
19  */
20
21 #include <sys/resource.h>
22 #include <sys/signal.h>
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "commands.h"
34 #include "top.h"
35 #include "machine.h"
36
37 static int err_compar(const void *p1, const void *p2);
38
39 struct errs             /* structure for a system-call error */
40 {
41     int  errnum;        /* value of errno (that is, the actual error) */
42     char *arg;          /* argument that caused the error */
43 };
44
45 static char *err_string(void);
46 static int str_adderr(char *str, int len, int err);
47 static int str_addarg(char *str, int len, char *arg, bool first);
48
49 /*
50  *  show_help() - display the help screen; invoked in response to
51  *              either 'h' or '?'.
52  */
53
54 const struct command all_commands[] =
55 {
56         {'C', "toggle the displaying of weighted CPU percentage", false, CMD_wcputog},
57         {'d', "change number of displays to show", false, CMD_displays},
58         {'e', "list errors generated by last \"kill\" or \"renice\" command", false, CMD_errors},
59         {'H', "toggle the displaying of threads", false, CMD_thrtog},
60         {'h', "show this help text", true, CMD_help},
61         {'?', NULL, true, CMD_help},
62         {'i', "toggle the displaying of idle processes", false, CMD_idletog},
63         {'I', NULL, false, CMD_idletog},
64         {'j', "toggle the displaying of jail ID", false, CMD_jidtog},
65         {'J', "display processes for only one jail (+ selects all jails)", false, CMD_jail},
66         {'k', "kill processes; send a signal to a list of processes", false, CMD_kill},
67         {'q', "quit" , true, CMD_quit},
68         {'m', "toggle the display between 'cpu' and 'io' modes", false, CMD_viewtog},
69         {'n', "change number of processes to display", false, CMD_number},
70         {'#', NULL, false, CMD_number},
71         {'o', "specify the sort order", false, CMD_order},
72         {'p', "display one process (+ selects all processes)", false, CMD_pid},
73         {'P', "toggle the displaying of per-CPU statistics", false, CMD_pcputog},
74         {'r', "renice a process", false, CMD_renice},
75         {'s', "change number of seconds to delay between updates", false, CMD_delay},
76         {'S', "toggle the displaying of system processes", false, CMD_viewsys},
77         {'a', "toggle the displaying of process titles", false, CMD_showargs},
78         {'T', "toggle the displaying of thread IDs", false, CMD_toggletid},
79         {'t', "toggle the display of this process", false, CMD_selftog},
80         {'u', "display processes for only one user (+ selects all users)", false, CMD_user},
81         {'w', "toggle the display of swap use for each process", false, CMD_swaptog},
82         {'z', "toggle the displaying of the system idle process", false, CMD_kidletog},
83         {' ', "update the display", false, CMD_update},
84         {0, NULL, true, CMD_NONE}
85 };
86
87 void
88 show_help(void)
89 {
90         const struct command *curcmd, *nextcmd;
91         char keys[8] = "";
92         _Static_assert(sizeof(keys) >= sizeof("a or b"), "keys right size");
93
94     printf("Top version FreeBSD, %s\n", copyright);
95         curcmd = all_commands;
96         while (curcmd->c != 0) {
97                 if (overstrike && !curcmd->available_to_dumb) {
98                         ++curcmd;
99                         continue;
100                 }
101                 if (curcmd->desc == NULL) {
102                         /* we already printed this */
103                         ++curcmd;
104                         continue;
105                 }
106                 nextcmd = curcmd + 1;
107                 if (nextcmd->desc == NULL && nextcmd->c != '\0') {
108                         sprintf(keys, "%c or %c", curcmd->c, nextcmd->c);
109                 } else if (curcmd->c == ' '){
110                         /* special case space rather than introducing a "display string" to
111                          * the struct */
112                         sprintf(keys, "SPC");
113                 } else {
114                         sprintf(keys, "%c", curcmd->c);
115                 }
116                 printf("%s\t- %s\n", keys, curcmd->desc);
117                 ++curcmd;
118         }
119     if (overstrike)
120     {
121                 fputs("\
122                                 Other commands are also available, but this terminal is not\n\
123                                 sophisticated enough to handle those commands gracefully.\n", stdout);
124     }
125 }
126
127 /*
128  *  Utility routines that help with some of the commands.
129  */
130
131 static char *
132 next_field(char *str)
133 {
134     if ((str = strchr(str, ' ')) == NULL)
135     {
136         return(NULL);
137     }
138     *str = '\0';
139     while (*++str == ' ') /* loop */;
140
141     /* if there is nothing left of the string, return NULL */
142     /* This fix is dedicated to Greg Earle */
143     return(*str == '\0' ? NULL : str);
144 }
145
146 static int
147 scanint(char *str, int *intp)
148 {
149     int val = 0;
150     char ch;
151
152     /* if there is nothing left of the string, flag it as an error */
153     /* This fix is dedicated to Greg Earle */
154     if (*str == '\0')
155     {
156         return(-1);
157     }
158
159     while ((ch = *str++) != '\0')
160     {
161         if (isdigit(ch))
162         {
163             val = val * 10 + (ch - '0');
164         }
165         else if (isspace(ch))
166         {
167             break;
168         }
169         else
170         {
171             return(-1);
172         }
173     }
174     *intp = val;
175     return(0);
176 }
177
178 /*
179  *  Some of the commands make system calls that could generate errors.
180  *  These errors are collected up in an array of structures for later
181  *  contemplation and display.  Such routines return a string containing an
182  *  error message, or NULL if no errors occurred.  The next few routines are
183  *  for manipulating and displaying these errors.  We need an upper limit on
184  *  the number of errors, so we arbitrarily choose 20.
185  */
186
187 #define ERRMAX 20
188
189 static struct errs errs[ERRMAX];
190 static int errcnt;
191 static char err_toomany[] = " too many errors occurred";
192 static char err_listem[] =
193         " Many errors occurred.  Press `e' to display the list of errors.";
194
195 /* These macros get used to reset and log the errors */
196 #define ERR_RESET   errcnt = 0
197 #define ERROR(p, e) if (errcnt >= ERRMAX) \
198                     { \
199                         return(err_toomany); \
200                     } \
201                     else \
202                     { \
203                         errs[errcnt].arg = (p); \
204                         errs[errcnt++].errnum = (e); \
205                     }
206
207 /*
208  *  err_string() - return an appropriate error string.  This is what the
209  *      command will return for displaying.  If no errors were logged, then
210  *      return NULL.  The maximum length of the error string is defined by
211  *      "STRMAX".
212  */
213
214 #define STRMAX 80
215
216 char *
217 err_string(void)
218 {
219     struct errs *errp;
220     int cnt = 0;
221     bool first = true;
222     int currerr = -1;
223     int stringlen;              /* characters still available in "string" */
224     static char string[STRMAX];
225
226     /* if there are no errors, return NULL */
227     if (errcnt == 0)
228     {
229         return(NULL);
230     }
231
232     /* sort the errors */
233     qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
234
235     /* need a space at the front of the error string */
236     string[0] = ' ';
237     string[1] = '\0';
238     stringlen = STRMAX - 2;
239
240     /* loop thru the sorted list, building an error string */
241     while (cnt < errcnt)
242     {
243         errp = &(errs[cnt++]);
244         if (errp->errnum != currerr)
245         {
246             if (currerr >= 0)
247             {
248                 if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
249                 {
250                     return(err_listem);
251                 }
252                 strcat(string, "; ");     /* we know there's more */
253             }
254             currerr = errp->errnum;
255             first = true;
256         }
257         if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
258         {
259             return(err_listem);
260         }
261         first = false;
262     }
263
264     /* add final message */
265     stringlen = str_adderr(string, stringlen, currerr);
266
267     /* return the error string */
268     return(stringlen == 0 ? err_listem : string);
269 }
270
271 /*
272  *  str_adderr(str, len, err) - add an explanation of error "err" to
273  *      the string "str".
274  */
275
276 static int
277 str_adderr(char *str, int len, int err)
278 {
279     const char *msg;
280     int msglen;
281
282     msg = err == 0 ? "Not a number" : strerror(err);
283     msglen = strlen(msg) + 2;
284     if (len <= msglen)
285     {
286         return(0);
287     }
288     strcat(str, ": ");
289     strcat(str, msg);
290     return(len - msglen);
291 }
292
293 /*
294  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
295  *      the string "str".  This is the first in the group when "first"
296  *      is set (indicating that a comma should NOT be added to the front).
297  */
298
299 static int
300 str_addarg(char str[], int len, char arg[], bool first)
301 {
302     int arglen;
303
304     arglen = strlen(arg);
305     if (!first)
306     {
307         arglen += 2;
308     }
309     if (len <= arglen)
310     {
311         return(0);
312     }
313     if (!first)
314     {
315         strcat(str, ", ");
316     }
317     strcat(str, arg);
318     return(len - arglen);
319 }
320
321 /*
322  *  err_compar(p1, p2) - comparison routine used by "qsort"
323  *      for sorting errors.
324  */
325
326 static int
327 err_compar(const void *p1, const void *p2)
328 {
329     int result;
330     const struct errs * const g1 = (const struct errs * const)p1;
331     const struct errs * const g2 = (const struct errs * const)p2;
332
333
334
335     if ((result = g1->errnum - g2->errnum) == 0)
336     {
337         return(strcmp(g1->arg, g2->arg));
338     }
339     return(result);
340 }
341
342 /*
343  *  error_count() - return the number of errors currently logged.
344  */
345
346 int
347 error_count(void)
348 {
349     return(errcnt);
350 }
351
352 /*
353  *  show_errors() - display on stdout the current log of errors.
354  */
355
356 void
357 show_errors(void)
358 {
359     int cnt = 0;
360     struct errs *errp = errs;
361
362     printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
363     while (cnt++ < errcnt)
364     {
365         printf("%5s: %s\n", errp->arg,
366             errp->errnum == 0 ? "Not a number" : strerror(errp->errnum));
367         errp++;
368     }
369 }
370
371 static const char no_proc_specified[] = " no processes specified";
372 static const char invalid_signal_number[] = " invalid_signal_number";
373 static const char bad_signal_name[] = " bad signal name";
374 static const char bad_pri_value[] = " bad priority value";
375
376 static int
377 signame_to_signum(const char * sig)
378 {
379         int n;
380
381         if (strncasecmp(sig, "SIG", 3) == 0)
382                 sig += 3;
383         for (n = 1; n < sys_nsig; n++) {
384             if (!strcasecmp(sys_signame[n], sig))
385                 return (n);
386         }
387         return (-1);
388 }
389
390 /*
391  *  kill_procs(str) - send signals to processes, much like the "kill"
392  *              command does; invoked in response to 'k'.
393  */
394
395 const char *
396 kill_procs(char *str)
397 {
398     char *nptr;
399     int signum = SIGTERM;       /* default */
400     int procnum;
401
402     /* reset error array */
403     ERR_RESET;
404
405     /* skip over leading white space */
406     while (isspace(*str)) str++;
407
408     if (str[0] == '-')
409     {
410         /* explicit signal specified */
411         if ((nptr = next_field(str)) == NULL)
412         {
413             return(no_proc_specified);
414         }
415
416         if (isdigit(str[1]))
417         {
418             scanint(str + 1, &signum);
419             if (signum <= 0 || signum >= NSIG)
420             {
421                 return(invalid_signal_number);
422             }
423         }
424         else
425         {
426                 signum = signame_to_signum(str + 1);
427
428             /* was it ever found */
429             if (signum == -1 )
430             {
431                         return(bad_signal_name);
432             }
433         }
434         /* put the new pointer in place */
435         str = nptr;
436     }
437
438     /* loop thru the string, killing processes */
439     do
440     {
441         if (scanint(str, &procnum) == -1)
442         {
443             ERROR(str, 0);
444         }
445         else
446         {
447             /* go in for the kill */
448             if (kill(procnum, signum) == -1)
449             {
450                 /* chalk up an error */
451                 ERROR(str, errno);
452             }
453         }
454     } while ((str = next_field(str)) != NULL);
455
456     /* return appropriate error string */
457     return(err_string());
458 }
459
460 /*
461  *  renice_procs(str) - change the "nice" of processes, much like the
462  *              "renice" command does; invoked in response to 'r'.
463  */
464
465 const char *
466 renice_procs(char *str)
467 {
468     char negate;
469     int prio;
470     int procnum;
471
472     ERR_RESET;
473
474     /* allow for negative priority values */
475     if ((negate = (*str == '-')) != 0)
476     {
477         /* move past the minus sign */
478         str++;
479     }
480
481     /* use procnum as a temporary holding place and get the number */
482     procnum = scanint(str, &prio);
483
484     /* negate if necessary */
485     if (negate)
486     {
487         prio = -prio;
488     }
489
490     /* check for validity */
491     if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
492     {
493         return(bad_pri_value);
494     }
495
496     /* move to the first process number */
497     if ((str = next_field(str)) == NULL)
498     {
499         return(no_proc_specified);
500     }
501
502     /* loop thru the process numbers, renicing each one */
503     do
504     {
505         if (scanint(str, &procnum) == -1)
506         {
507             ERROR(str, 0);
508         }
509
510         if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
511         {
512             ERROR(str, errno);
513         }
514     } while ((str = next_field(str)) != NULL);
515
516     /* return appropriate error string */
517     return(err_string());
518 }
519