| 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 | } |