File: | src/usr.bin/top/commands.c |
Warning: | line 370, column 8 Assigned value is garbage or undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: commands.c,v 1.33 2019/10/08 07:26:59 kn Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Top users/processes display for Unix | |||
5 | * Version 3 | |||
6 | * | |||
7 | * Copyright (c) 1984, 1989, William LeFebvre, Rice University | |||
8 | * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University | |||
9 | * | |||
10 | * Redistribution and use in source and binary forms, with or without | |||
11 | * modification, are permitted provided that the following conditions | |||
12 | * are met: | |||
13 | * 1. Redistributions of source code must retain the above copyright | |||
14 | * notice, this list of conditions and the following disclaimer. | |||
15 | * 2. Redistributions in binary form must reproduce the above copyright | |||
16 | * notice, this list of conditions and the following disclaimer in the | |||
17 | * documentation and/or other materials provided with the distribution. | |||
18 | * | |||
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||
20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||
22 | * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
29 | */ | |||
30 | ||||
31 | /* | |||
32 | * This file contains the routines that implement some of the interactive | |||
33 | * mode commands. Note that some of the commands are implemented in-line | |||
34 | * in "main". This is necessary because they change the global state of | |||
35 | * "top" (i.e.: changing the number of processes to display). | |||
36 | */ | |||
37 | ||||
38 | #include <sys/types.h> | |||
39 | #include <stdio.h> | |||
40 | #include <err.h> | |||
41 | #include <ctype.h> | |||
42 | #include <errno(*__errno()).h> | |||
43 | #include <stdlib.h> | |||
44 | #include <string.h> | |||
45 | #include <signal.h> | |||
46 | #include <unistd.h> | |||
47 | #include <sys/time.h> | |||
48 | #include <sys/resource.h> | |||
49 | #include <stdbool.h> | |||
50 | ||||
51 | #include "top.h" | |||
52 | ||||
53 | #include "utils.h" | |||
54 | #include "machine.h" | |||
55 | ||||
56 | static char *next_field(char *); | |||
57 | static int scan_arg(char *, int *, char *); | |||
58 | static char *err_string(void); | |||
59 | static size_t str_adderr(char *, size_t, int); | |||
60 | static size_t str_addarg(char *, size_t, char *, int); | |||
61 | static int err_compar(const void *, const void *); | |||
62 | ||||
63 | /* | |||
64 | * Utility routines that help with some of the commands. | |||
65 | */ | |||
66 | static char * | |||
67 | next_field(char *str) | |||
68 | { | |||
69 | char *spaces = " \t"; | |||
70 | size_t span; | |||
71 | ||||
72 | span = strcspn(str, spaces); | |||
73 | if (span == strlen(str)) | |||
74 | return (NULL((void *)0)); | |||
75 | ||||
76 | str += span; | |||
77 | *str++ = '\0'; | |||
78 | ||||
79 | while (strcspn(str, spaces) == 0) | |||
80 | str++; | |||
81 | ||||
82 | if (*str == '\0') | |||
83 | return (NULL((void *)0)); | |||
84 | ||||
85 | return(str); | |||
86 | } | |||
87 | ||||
88 | /* | |||
89 | * Scan the renice or kill interactive arguments for data and/or errors. | |||
90 | */ | |||
91 | static int | |||
92 | scan_arg(char *str, int *intp, char *nptr) | |||
93 | { | |||
94 | int val = 0, bad_flag = 0; | |||
95 | char ch; | |||
96 | ||||
97 | *nptr = '\0'; | |||
98 | ||||
99 | if (*str == '\0') | |||
100 | return (-1); | |||
101 | ||||
102 | while ((ch = *str++) != '\0') { | |||
103 | if (isspace((unsigned char)ch)) | |||
104 | break; | |||
105 | else if (!isdigit((unsigned char)ch)) | |||
106 | bad_flag = 1; | |||
107 | else | |||
108 | val = val * 10 + (ch - '0'); | |||
109 | ||||
110 | *(nptr++) = ch; | |||
111 | } | |||
112 | *nptr = '\0'; | |||
113 | ||||
114 | if (bad_flag == 1) | |||
115 | return(-1); | |||
116 | ||||
117 | *intp = val; | |||
118 | return (0); | |||
119 | } | |||
120 | ||||
121 | /* | |||
122 | * Some of the commands make system calls that could generate errors. | |||
123 | * These errors are collected up in an array of structures for later | |||
124 | * contemplation and display. Such routines return a string containing an | |||
125 | * error message, or NULL if no errors occurred. The next few routines are | |||
126 | * for manipulating and displaying these errors. We need an upper limit on | |||
127 | * the number of errors, so we arbitrarily choose 20. | |||
128 | */ | |||
129 | ||||
130 | #define ERRMAX20 20 | |||
131 | ||||
132 | struct errs errs[ERRMAX20]; | |||
133 | int errcnt; | |||
134 | static char *err_toomany = " too many errors occurred"; | |||
135 | static char *err_listem = | |||
136 | " Many errors occurred. Press `e' to display the list of errors."; | |||
137 | ||||
138 | /* These macros get used to reset and log the errors */ | |||
139 | #define ERR_RESETerrcnt = 0 errcnt = 0 | |||
140 | #define ERROR(p, e)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(p)) == ((void * )0)) err(1, "strdup"); errs[errcnt++].err = (e); } \ | |||
141 | if (errcnt >= ERRMAX20) { \ | |||
142 | return(err_toomany); \ | |||
143 | } else { \ | |||
144 | free(errs[errcnt].arg); \ | |||
145 | if ((errs[errcnt].arg = strdup(p)) == NULL((void *)0)) \ | |||
146 | err(1, "strdup"); \ | |||
147 | errs[errcnt++].err = (e); \ | |||
148 | } | |||
149 | ||||
150 | #define STRMAX80 80 | |||
151 | ||||
152 | /* | |||
153 | * err_string() - return an appropriate error string. This is what the | |||
154 | * command will return for displaying. If no errors were logged, then | |||
155 | * return NULL. The maximum length of the error string is defined by | |||
156 | * "STRMAX". | |||
157 | */ | |||
158 | static char * | |||
159 | err_string(void) | |||
160 | { | |||
161 | int cnt = 0, first = true1, currerr = -1; | |||
162 | static char string[STRMAX80]; | |||
163 | struct errs *errp; | |||
164 | ||||
165 | /* if there are no errors, return NULL */ | |||
166 | if (errcnt == 0) | |||
167 | return (NULL((void *)0)); | |||
168 | ||||
169 | /* sort the errors */ | |||
170 | qsort(errs, errcnt, sizeof(struct errs), err_compar); | |||
171 | ||||
172 | /* need a space at the front of the error string */ | |||
173 | string[0] = ' '; | |||
174 | string[1] = '\0'; | |||
175 | ||||
176 | /* loop thru the sorted list, building an error string */ | |||
177 | while (cnt < errcnt) { | |||
178 | errp = &(errs[cnt++]); | |||
179 | if (errp->err != currerr) { | |||
180 | if (currerr != -1) { | |||
181 | if (str_adderr(string, sizeof string, currerr) > | |||
182 | sizeof string - 2) | |||
183 | return (err_listem); | |||
184 | ||||
185 | /* we know there's more */ | |||
186 | (void) strlcat(string, "; ", sizeof string); | |||
187 | } | |||
188 | currerr = errp->err; | |||
189 | first = true1; | |||
190 | } | |||
191 | if (str_addarg(string, sizeof string, errp->arg, first) >= | |||
192 | sizeof string) | |||
193 | return (err_listem); | |||
194 | ||||
195 | first = false0; | |||
196 | } | |||
197 | ||||
198 | /* add final message */ | |||
199 | if (str_adderr(string, sizeof string, currerr) >= sizeof string) | |||
200 | return (err_listem); | |||
201 | ||||
202 | /* return the error string */ | |||
203 | return (string); | |||
204 | } | |||
205 | ||||
206 | /* | |||
207 | * str_adderr(str, len, err) - add an explanation of error "err" to | |||
208 | * the string "str". | |||
209 | */ | |||
210 | static size_t | |||
211 | str_adderr(char *str, size_t len, int err) | |||
212 | { | |||
213 | size_t msglen; | |||
214 | char *msg; | |||
215 | ||||
216 | msg = err == 0 ? "Not a number" : strerror(err); | |||
217 | ||||
218 | if ((msglen = strlcat(str, ": ", len)) >= len) | |||
219 | return (msglen); | |||
220 | ||||
221 | return (strlcat(str, msg, len)); | |||
222 | } | |||
223 | ||||
224 | /* | |||
225 | * str_addarg(str, len, arg, first) - add the string argument "arg" to | |||
226 | * the string "str". This is the first in the group when "first" | |||
227 | * is set (indicating that a comma should NOT be added to the front). | |||
228 | */ | |||
229 | static size_t | |||
230 | str_addarg(char *str, size_t len, char *arg, int first) | |||
231 | { | |||
232 | size_t msglen; | |||
233 | ||||
234 | if (!first) { | |||
235 | if ((msglen = strlcat(str, ", ", len)) >= len) | |||
236 | return (msglen); | |||
237 | } | |||
238 | return (strlcat(str, arg, len)); | |||
239 | } | |||
240 | ||||
241 | /* | |||
242 | * err_compar(p1, p2) - comparison routine used by "qsort" | |||
243 | * for sorting errors. | |||
244 | */ | |||
245 | static int | |||
246 | err_compar(const void *e1, const void *e2) | |||
247 | { | |||
248 | const struct errs *p1 = (const struct errs *) e1; | |||
249 | const struct errs *p2 = (const struct errs *) e2; | |||
250 | int result; | |||
251 | ||||
252 | if ((result = p1->err - p2->err) == 0) | |||
253 | return (strcmp(p1->arg, p2->arg)); | |||
254 | return (result); | |||
255 | } | |||
256 | ||||
257 | /* | |||
258 | * error_count() - return the number of errors currently logged. | |||
259 | */ | |||
260 | int | |||
261 | error_count(void) | |||
262 | { | |||
263 | return (errcnt); | |||
264 | } | |||
265 | ||||
266 | /* | |||
267 | * kill_procs(str) - send signals to processes, much like the "kill" | |||
268 | * command does; invoked in response to 'k'. | |||
269 | */ | |||
270 | char * | |||
271 | kill_procs(char *str) | |||
272 | { | |||
273 | int signum = SIGTERM15, procnum; | |||
274 | uid_t uid, puid; | |||
275 | char tempbuf[TEMPBUFSIZE50]; | |||
276 | char *nptr, *tmp; | |||
277 | ||||
278 | tmp = tempbuf; | |||
279 | ||||
280 | /* reset error array */ | |||
281 | ERR_RESETerrcnt = 0; | |||
282 | ||||
283 | /* remember our uid */ | |||
284 | uid = getuid(); | |||
285 | ||||
286 | /* skip over leading white space */ | |||
287 | while (isspace((unsigned char)*str)) | |||
288 | str++; | |||
289 | ||||
290 | if (*str == '-') { | |||
291 | str++; | |||
292 | ||||
293 | /* explicit signal specified */ | |||
294 | if ((nptr = next_field(str)) == NULL((void *)0)) | |||
295 | return (" kill: no processes specified"); | |||
296 | ||||
297 | if (isdigit((unsigned char)*str)) { | |||
298 | (void) scan_arg(str, &signum, tmp); | |||
299 | if (signum <= 0 || signum >= NSIG33) | |||
300 | return (" invalid signal number"); | |||
301 | } else { | |||
302 | /* translate the name into a number */ | |||
303 | for (signum = 0; signum < NSIG33; signum++) { | |||
304 | if (strcasecmp(sys_signame[signum], | |||
305 | str) == 0) | |||
306 | break; | |||
307 | } | |||
308 | ||||
309 | /* was it ever found */ | |||
310 | if (signum == NSIG33) | |||
311 | return (" bad signal name"); | |||
312 | } | |||
313 | /* put the new pointer in place */ | |||
314 | str = nptr; | |||
315 | } | |||
316 | nptr = tempbuf; | |||
317 | /* loop thru the string, killing processes */ | |||
318 | do { | |||
319 | if (scan_arg(str, &procnum, nptr) == -1) { | |||
320 | ERROR(nptr, 0)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = (0); }; | |||
321 | } else { | |||
322 | /* check process owner if we're not root */ | |||
323 | puid = proc_owner(procnum); | |||
324 | if (puid == (uid_t)(-1)) { | |||
325 | ERROR(nptr, ESRCH)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = (3); }; | |||
326 | } else if (uid && (uid != puid)) { | |||
327 | ERROR(nptr, EACCES)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = (13); }; | |||
328 | } else if (kill(procnum, signum) == -1) { | |||
329 | ERROR(nptr, errno)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = ((*__errno())); }; | |||
330 | } | |||
331 | } | |||
332 | } while ((str = next_field(str)) != NULL((void *)0)); | |||
333 | ||||
334 | /* return appropriate error string */ | |||
335 | return (err_string()); | |||
336 | } | |||
337 | ||||
338 | /* | |||
339 | * renice_procs(str) - change the "nice" of processes, much like the | |||
340 | * "renice" command does; invoked in response to 'r'. | |||
341 | */ | |||
342 | char * | |||
343 | renice_procs(char *str) | |||
344 | { | |||
345 | uid_t uid; | |||
346 | char negate; | |||
347 | int prio, procnum; | |||
348 | char tempbuf[TEMPBUFSIZE50]; | |||
349 | char *nptr; | |||
350 | ||||
351 | ERR_RESETerrcnt = 0; | |||
352 | uid = getuid(); | |||
353 | ||||
354 | /* skip over leading white space */ | |||
355 | while (isspace((unsigned char)*str)) | |||
| ||||
356 | str++; | |||
357 | ||||
358 | /* allow for negative priority values */ | |||
359 | if ((negate = (*str == '-')) != 0) { | |||
360 | /* move past the minus sign */ | |||
361 | str++; | |||
362 | } | |||
363 | ||||
364 | nptr = tempbuf; | |||
365 | /* use procnum as a temporary holding place and get the number */ | |||
366 | procnum = scan_arg(str, &prio, nptr); | |||
367 | ||||
368 | /* negate if necessary */ | |||
369 | if (negate
| |||
370 | prio = -prio; | |||
| ||||
371 | ||||
372 | #if defined(PRIO_MIN(-20)) && defined(PRIO_MAX20) | |||
373 | /* check for validity */ | |||
374 | if (procnum == -1 || prio < PRIO_MIN(-20) || prio > PRIO_MAX20) | |||
375 | return (" bad priority value"); | |||
376 | #endif | |||
377 | ||||
378 | /* move to the first process number */ | |||
379 | if ((str = next_field(str)) == NULL((void *)0)) | |||
380 | return (" no processes specified"); | |||
381 | ||||
382 | /* loop thru the process numbers, renicing each one */ | |||
383 | do { | |||
384 | if (scan_arg(str, &procnum, nptr) == -1) { | |||
385 | ERROR(nptr, 0)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = (0); }; | |||
386 | } | |||
387 | /* check process owner if we're not root */ | |||
388 | else if (uid && (uid != proc_owner(procnum))) { | |||
389 | ERROR(nptr, EACCES)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = (13); }; | |||
390 | } else if (setpriority(PRIO_PROCESS0, procnum, prio) == -1) { | |||
391 | ERROR(nptr, errno)if (errcnt >= 20) { return(err_toomany); } else { free(errs [errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void *)0)) err(1, "strdup"); errs[errcnt++].err = ((*__errno())); }; | |||
392 | } | |||
393 | } while ((str = next_field(str)) != NULL((void *)0)); | |||
394 | ||||
395 | /* return appropriate error string */ | |||
396 | return (err_string()); | |||
397 | } |