2 * Top users/processes display for Unix
5 * This program may be freely redistributed,
6 * but this entire comment MUST remain intact.
8 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
9 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
11 * $FreeBSD: src/contrib/top/commands.c,v 1.4.6.1 2002/08/11 17:09:25 dwmalone Exp $
12 * $DragonFly: src/contrib/top/commands.c,v 1.3 2006/02/15 12:54:36 corecode Exp $
16 * This file contains the routines that implement some of the interactive
17 * mode commands. Note that some of the commands are implemented in-line
18 * in "main". This is necessary because they change the global state of
19 * "top" (i.e.: changing the number of processes to display).
28 #include <sys/resource.h>
30 #include "sigdesc.h" /* generated automatically */
38 extern const char *copyright
;
40 /* imported from screen.c */
41 extern int overstrike
;
43 static int err_compar(const void *, const void *);
44 static const char *err_string(void);
45 static int str_adderr(char *str
, int len
, int err
);
46 static int str_addarg(char *str
, int len
, char *arg
, int first
);
49 * show_help() - display the help screen; invoked in response to
56 printf("Top version %s, %s\n", version_string(), copyright
);
58 A top users display for Unix\n\
60 These single-character commands are available:\n\
64 h or ? - help; show this text\n", stdout
);
66 /* not all commands are availalbe with overstrike terminals */
70 Other commands are also available, but this terminal is not\n\
71 sophisticated enough to handle those commands gracefully.\n\n", stdout
);
76 d - change number of displays to show\n\
77 e - list errors generated by last \"kill\" or \"renice\" command\n\
78 i - toggle the displaying of idle processes\n\
80 O - only display threads\n\
81 T - toggle the displaying of threads\n\
82 S - toggle the displaying of system processes\n\
83 k - kill processes; send a signal to a list of processes\n\
84 n or # - change number of processes to display\n", stdout
);
87 o - specify sort order (pri, size, res, cpu, time, thr)\n", stdout
);
90 r - renice a process\n\
91 s - change number of seconds to delay between updates\n\
92 u - display processes for only one user (+ selects all users)\n\
99 * Utility routines that help with some of the commands.
103 next_field(char *str
)
105 if ((str
= strchr(str
, ' ')) == NULL
)
110 while (*++str
== ' ') /* loop */;
112 /* if there is nothing left of the string, return NULL */
113 /* This fix is dedicated to Greg Earle */
114 return(*str
== '\0' ? NULL
: str
);
118 scanint(char *str
, int *intp
)
120 register int val
= 0;
123 /* if there is nothing left of the string, flag it as an error */
124 /* This fix is dedicated to Greg Earle */
130 while ((ch
= *str
++) != '\0')
134 val
= val
* 10 + (ch
- '0');
136 else if (isspace(ch
))
150 * Some of the commands make system calls that could generate errors.
151 * These errors are collected up in an array of structures for later
152 * contemplation and display. Such routines return a string containing an
153 * error message, or NULL if no errors occurred. The next few routines are
154 * for manipulating and displaying these errors. We need an upper limit on
155 * the number of errors, so we arbitrarily choose 20.
160 struct errs
/* structure for a system-call error */
162 int errnum
; /* value of errno (that is, the actual error) */
163 char *arg
; /* argument that caused the error */
166 static struct errs errs
[ERRMAX
];
168 static const char *err_toomany
= " too many errors occurred";
169 static const char *err_listem
=
170 " Many errors occurred. Press `e' to display the list of errors.";
172 /* These macros get used to reset and log the errors */
173 #define ERR_RESET errcnt = 0
174 #define ERROR(p, e) if (errcnt >= ERRMAX) \
176 return(err_toomany); \
180 errs[errcnt].arg = (p); \
181 errs[errcnt++].errnum = (e); \
185 * err_string() - return an appropriate error string. This is what the
186 * command will return for displaying. If no errors were logged, then
187 * return NULL. The maximum length of the error string is defined by
200 int stringlen
; /* characters still available in "string" */
201 static char string
[STRMAX
];
203 /* if there are no errors, return NULL */
209 /* sort the errors */
210 qsort((char *)errs
, errcnt
, sizeof(struct errs
), err_compar
);
212 /* need a space at the front of the error string */
215 stringlen
= STRMAX
- 2;
217 /* loop thru the sorted list, building an error string */
220 errp
= &(errs
[cnt
++]);
221 if (errp
->errnum
!= currerr
)
225 if ((stringlen
= str_adderr(string
, stringlen
, currerr
)) < 2)
229 (void) strcat(string
, "; "); /* we know there's more */
231 currerr
= errp
->errnum
;
234 if ((stringlen
= str_addarg(string
, stringlen
, errp
->arg
, first
)) ==0)
241 /* add final message */
242 stringlen
= str_adderr(string
, stringlen
, currerr
);
244 /* return the error string */
245 return(stringlen
== 0 ? err_listem
: string
);
249 * str_adderr(str, len, err) - add an explanation of error "err" to
254 str_adderr(char *str
, int len
, int err
)
259 msg
= err
== 0 ? "Not a number" : errmsg(err
);
260 msglen
= strlen(msg
) + 2;
265 (void) strcat(str
, ": ");
266 (void) strcat(str
, msg
);
267 return(len
- msglen
);
271 * str_addarg(str, len, arg, first) - add the string argument "arg" to
272 * the string "str". This is the first in the group when "first"
273 * is set (indicating that a comma should NOT be added to the front).
276 str_addarg(char *str
, int len
, char *arg
, int first
)
280 arglen
= strlen(arg
);
291 (void) strcat(str
, ", ");
293 (void) strcat(str
, arg
);
294 return(len
- arglen
);
298 * err_compar(p1, p2) - comparison routine used by "qsort"
299 * for sorting errors.
302 err_compar(const void *arg1
, const void *arg2
)
304 const struct errs
*p1
= arg1
;
305 const struct errs
*p2
= arg2
;
308 if ((result
= p1
->errnum
- p2
->errnum
) == 0)
310 return(strcmp(p1
->arg
, p2
->arg
));
316 * error_count() - return the number of errors currently logged.
326 * show_errors() - display on stdout the current log of errors.
332 register int cnt
= 0;
333 register struct errs
*errp
= errs
;
335 printf("%d error%s:\n\n", errcnt
, errcnt
== 1 ? "" : "s");
336 while (cnt
++ < errcnt
)
338 printf("%5s: %s\n", errp
->arg
,
339 errp
->errnum
== 0 ? "Not a number" : errmsg(errp
->errnum
));
345 * kill_procs(str) - send signals to processes, much like the "kill"
346 * command does; invoked in response to 'k'.
350 kill_procs(char *str
)
353 int signum
= SIGTERM
; /* default */
355 struct sigdesc
*sigp
;
358 /* reset error array */
361 /* remember our uid */
364 /* skip over leading white space */
365 while (isspace(*str
)) str
++;
369 /* explicit signal specified */
370 if ((nptr
= next_field(str
)) == NULL
)
372 return(" kill: no processes specified");
377 (void) scanint(str
+ 1, &signum
);
378 if (signum
<= 0 || signum
>= NSIG
)
380 return(" invalid signal number");
385 /* translate the name into a number */
386 for (sigp
= sigdesc
; sigp
->name
!= NULL
; sigp
++)
388 if (strcmp(sigp
->name
, str
+ 1) == 0)
390 signum
= sigp
->number
;
395 /* was it ever found */
396 if (sigp
->name
== NULL
)
398 return(" bad signal name");
401 /* put the new pointer in place */
405 /* loop thru the string, killing processes */
408 if (scanint(str
, &procnum
) == -1)
414 /* check process owner if we're not root */
415 if (uid
&& (uid
!= proc_owner(procnum
)))
419 /* go in for the kill */
420 else if (kill(procnum
, signum
) == -1)
422 /* chalk up an error */
426 } while ((str
= next_field(str
)) != NULL
);
428 /* return appropriate error string */
429 return(err_string());
433 * renice_procs(str) - change the "nice" of processes, much like the
434 * "renice" command does; invoked in response to 'r'.
438 renice_procs(char *str
)
440 register char negate
;
448 /* allow for negative priority values */
449 if ((negate
= (*str
== '-')) != 0)
451 /* move past the minus sign */
455 /* use procnum as a temporary holding place and get the number */
456 procnum
= scanint(str
, &prio
);
458 /* negate if necessary */
464 #if defined(PRIO_MIN) && defined(PRIO_MAX)
465 /* check for validity */
466 if (procnum
== -1 || prio
< PRIO_MIN
|| prio
> PRIO_MAX
)
468 return(" bad priority value");
472 /* move to the first process number */
473 if ((str
= next_field(str
)) == NULL
)
475 return(" no processes specified");
478 /* loop thru the process numbers, renicing each one */
481 if (scanint(str
, &procnum
) == -1)
486 /* check process owner if we're not root */
487 else if (uid
&& (uid
!= proc_owner(procnum
)))
491 else if (setpriority(PRIO_PROCESS
, procnum
, prio
) == -1)
495 } while ((str
= next_field(str
)) != NULL
);
497 /* return appropriate error string */
498 return(err_string());