diff options
| author | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-11-16 15:43:41 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-16 15:43:41 +0100 |
| commit | 8c2fe21c3a3a392b084fd5a67bc5f250c2a9ec34 (patch) | |
| tree | 8bfe05f89b1ef63b94c9d33180a77c3a2c537d13 /lib | |
| parent | 2510d9ad5851c669ace7cfc16ea3ff9bf2c86106 (diff) | |
| parent | 584272e97d5c72ad6a7fb9b91844592252040ed9 (diff) | |
| download | monitoring-plugins-8c2fe21c3a3a392b084fd5a67bc5f250c2a9ec34.tar.gz | |
Merge pull request #2177 from RincewindsHat/modern_output/check_by_sshHEADmastercoverity/master
Modern output/check by ssh
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/utils_cmd.c | 300 | ||||
| -rw-r--r-- | lib/utils_cmd.h | 10 |
2 files changed, 300 insertions, 10 deletions
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 35b83297..42c81793 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c | |||
| @@ -56,6 +56,7 @@ static pid_t *_cmd_pids = NULL; | |||
| 56 | #include "./maxfd.h" | 56 | #include "./maxfd.h" |
| 57 | 57 | ||
| 58 | #include <fcntl.h> | 58 | #include <fcntl.h> |
| 59 | #include <stddef.h> | ||
| 59 | 60 | ||
| 60 | #ifdef HAVE_SYS_WAIT_H | 61 | #ifdef HAVE_SYS_WAIT_H |
| 61 | # include <sys/wait.h> | 62 | # include <sys/wait.h> |
| @@ -79,7 +80,8 @@ static pid_t *_cmd_pids = NULL; | |||
| 79 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) | 80 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) |
| 80 | __attribute__((__nonnull__(1, 2, 3))); | 81 | __attribute__((__nonnull__(1, 2, 3))); |
| 81 | 82 | ||
| 82 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2))); | 83 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) |
| 84 | __attribute__((__nonnull__(2))); | ||
| 83 | 85 | ||
| 84 | static int _cmd_close(int fileDescriptor); | 86 | static int _cmd_close(int fileDescriptor); |
| 85 | 87 | ||
| @@ -102,14 +104,85 @@ void cmd_init(void) { | |||
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 107 | typedef struct { | ||
| 108 | int stdout_pipe_fd[2]; | ||
| 109 | int stderr_pipe_fd[2]; | ||
| 110 | int file_descriptor; | ||
| 111 | int error_code; | ||
| 112 | } int_cmd_open_result; | ||
| 113 | static int_cmd_open_result _cmd_open2(char *const *argv) { | ||
| 114 | #ifdef RLIMIT_CORE | ||
| 115 | struct rlimit limit; | ||
| 116 | #endif | ||
| 117 | |||
| 118 | if (!_cmd_pids) { | ||
| 119 | CMD_INIT; | ||
| 120 | } | ||
| 121 | |||
| 122 | setenv("LC_ALL", "C", 1); | ||
| 123 | |||
| 124 | int_cmd_open_result result = { | ||
| 125 | .error_code = 0, | ||
| 126 | .stdout_pipe_fd = {0, 0}, | ||
| 127 | .stderr_pipe_fd = {0, 0}, | ||
| 128 | }; | ||
| 129 | pid_t pid; | ||
| 130 | if (pipe(result.stdout_pipe_fd) < 0 || pipe(result.stderr_pipe_fd) < 0 || (pid = fork()) < 0) { | ||
| 131 | result.error_code = -1; | ||
| 132 | return result; /* errno set by the failing function */ | ||
| 133 | } | ||
| 134 | |||
| 135 | /* child runs exceve() and _exit. */ | ||
| 136 | if (pid == 0) { | ||
| 137 | #ifdef RLIMIT_CORE | ||
| 138 | /* the program we execve shouldn't leave core files */ | ||
| 139 | getrlimit(RLIMIT_CORE, &limit); | ||
| 140 | limit.rlim_cur = 0; | ||
| 141 | setrlimit(RLIMIT_CORE, &limit); | ||
| 142 | #endif | ||
| 143 | close(result.stdout_pipe_fd[0]); | ||
| 144 | if (result.stdout_pipe_fd[1] != STDOUT_FILENO) { | ||
| 145 | dup2(result.stdout_pipe_fd[1], STDOUT_FILENO); | ||
| 146 | close(result.stdout_pipe_fd[1]); | ||
| 147 | } | ||
| 148 | close(result.stderr_pipe_fd[0]); | ||
| 149 | if (result.stderr_pipe_fd[1] != STDERR_FILENO) { | ||
| 150 | dup2(result.stderr_pipe_fd[1], STDERR_FILENO); | ||
| 151 | close(result.stderr_pipe_fd[1]); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* close all descriptors in _cmd_pids[] | ||
| 155 | * This is executed in a separate address space (pure child), | ||
| 156 | * so we don't have to worry about async safety */ | ||
| 157 | long maxfd = mp_open_max(); | ||
| 158 | for (int i = 0; i < maxfd; i++) { | ||
| 159 | if (_cmd_pids[i] > 0) { | ||
| 160 | close(i); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | execve(argv[0], argv, environ); | ||
| 165 | _exit(STATE_UNKNOWN); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* parent picks up execution here */ | ||
| 169 | /* close children descriptors in our address space */ | ||
| 170 | close(result.stdout_pipe_fd[1]); | ||
| 171 | close(result.stderr_pipe_fd[1]); | ||
| 172 | |||
| 173 | /* tag our file's entry in the pid-list and return it */ | ||
| 174 | _cmd_pids[result.stdout_pipe_fd[0]] = pid; | ||
| 175 | |||
| 176 | result.file_descriptor = result.stdout_pipe_fd[0]; | ||
| 177 | return result; | ||
| 178 | } | ||
| 179 | |||
| 105 | /* Start running a command, array style */ | 180 | /* Start running a command, array style */ |
| 106 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { | 181 | static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { |
| 107 | #ifdef RLIMIT_CORE | 182 | #ifdef RLIMIT_CORE |
| 108 | struct rlimit limit; | 183 | struct rlimit limit; |
| 109 | #endif | 184 | #endif |
| 110 | 185 | ||
| 111 | int i = 0; | ||
| 112 | |||
| 113 | if (!_cmd_pids) { | 186 | if (!_cmd_pids) { |
| 114 | CMD_INIT; | 187 | CMD_INIT; |
| 115 | } | 188 | } |
| @@ -144,7 +217,7 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { | |||
| 144 | * This is executed in a separate address space (pure child), | 217 | * This is executed in a separate address space (pure child), |
| 145 | * so we don't have to worry about async safety */ | 218 | * so we don't have to worry about async safety */ |
| 146 | long maxfd = mp_open_max(); | 219 | long maxfd = mp_open_max(); |
| 147 | for (i = 0; i < maxfd; i++) { | 220 | for (int i = 0; i < maxfd; i++) { |
| 148 | if (_cmd_pids[i] > 0) { | 221 | if (_cmd_pids[i] > 0) { |
| 149 | close(i); | 222 | close(i); |
| 150 | } | 223 | } |
| @@ -192,6 +265,87 @@ static int _cmd_close(int fileDescriptor) { | |||
| 192 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; | 265 | return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; |
| 193 | } | 266 | } |
| 194 | 267 | ||
| 268 | typedef struct { | ||
| 269 | int error_code; | ||
| 270 | output output_container; | ||
| 271 | } int_cmd_fetch_output2; | ||
| 272 | static int_cmd_fetch_output2 _cmd_fetch_output2(int fileDescriptor, int flags) { | ||
| 273 | char tmpbuf[4096]; | ||
| 274 | |||
| 275 | int_cmd_fetch_output2 result = { | ||
| 276 | .error_code = 0, | ||
| 277 | .output_container = | ||
| 278 | { | ||
| 279 | .buf = NULL, | ||
| 280 | .buflen = 0, | ||
| 281 | .line = NULL, | ||
| 282 | .lines = 0, | ||
| 283 | }, | ||
| 284 | }; | ||
| 285 | ssize_t ret; | ||
| 286 | while ((ret = read(fileDescriptor, tmpbuf, sizeof(tmpbuf))) > 0) { | ||
| 287 | size_t len = (size_t)ret; | ||
| 288 | result.output_container.buf = | ||
| 289 | realloc(result.output_container.buf, result.output_container.buflen + len + 1); | ||
| 290 | memcpy(result.output_container.buf + result.output_container.buflen, tmpbuf, len); | ||
| 291 | result.output_container.buflen += len; | ||
| 292 | } | ||
| 293 | |||
| 294 | if (ret < 0) { | ||
| 295 | printf("read() returned %zd: %s\n", ret, strerror(errno)); | ||
| 296 | result.error_code = -1; | ||
| 297 | return result; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* some plugins may want to keep output unbroken, and some commands | ||
| 301 | * will yield no output, so return here for those */ | ||
| 302 | if (flags & CMD_NO_ARRAYS || !result.output_container.buf || !result.output_container.buflen) { | ||
| 303 | return result; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* and some may want both */ | ||
| 307 | char *buf = NULL; | ||
| 308 | if (flags & CMD_NO_ASSOC) { | ||
| 309 | buf = malloc(result.output_container.buflen); | ||
| 310 | memcpy(buf, result.output_container.buf, result.output_container.buflen); | ||
| 311 | } else { | ||
| 312 | buf = result.output_container.buf; | ||
| 313 | } | ||
| 314 | |||
| 315 | result.output_container.line = NULL; | ||
| 316 | size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ | ||
| 317 | size_t rsf = 6; | ||
| 318 | size_t lineno = 0; | ||
| 319 | for (size_t i = 0; i < result.output_container.buflen;) { | ||
| 320 | /* make sure we have enough memory */ | ||
| 321 | if (lineno >= ary_size) { | ||
| 322 | /* ary_size must never be zero */ | ||
| 323 | do { | ||
| 324 | ary_size = result.output_container.buflen >> --rsf; | ||
| 325 | } while (!ary_size); | ||
| 326 | |||
| 327 | result.output_container.line = | ||
| 328 | realloc(result.output_container.line, ary_size * sizeof(char *)); | ||
| 329 | } | ||
| 330 | |||
| 331 | /* set the pointer to the string */ | ||
| 332 | result.output_container.line[lineno] = &buf[i]; | ||
| 333 | |||
| 334 | /* hop to next newline or end of buffer */ | ||
| 335 | while (buf[i] != '\n' && i < result.output_container.buflen) { | ||
| 336 | i++; | ||
| 337 | } | ||
| 338 | buf[i] = '\0'; | ||
| 339 | |||
| 340 | lineno++; | ||
| 341 | i++; | ||
| 342 | } | ||
| 343 | |||
| 344 | result.output_container.lines = lineno; | ||
| 345 | |||
| 346 | return result; | ||
| 347 | } | ||
| 348 | |||
| 195 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { | 349 | static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { |
| 196 | char tmpbuf[4096]; | 350 | char tmpbuf[4096]; |
| 197 | cmd_output->buf = NULL; | 351 | cmd_output->buf = NULL; |
| @@ -225,7 +379,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) | |||
| 225 | } | 379 | } |
| 226 | 380 | ||
| 227 | cmd_output->line = NULL; | 381 | cmd_output->line = NULL; |
| 228 | cmd_output->lens = NULL; | ||
| 229 | size_t i = 0; | 382 | size_t i = 0; |
| 230 | size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ | 383 | size_t ary_size = 0; /* rsf = right shift factor, dec'ed uncond once */ |
| 231 | size_t rsf = 6; | 384 | size_t rsf = 6; |
| @@ -239,7 +392,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) | |||
| 239 | } while (!ary_size); | 392 | } while (!ary_size); |
| 240 | 393 | ||
| 241 | cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); | 394 | cmd_output->line = realloc(cmd_output->line, ary_size * sizeof(char *)); |
| 242 | cmd_output->lens = realloc(cmd_output->lens, ary_size * sizeof(size_t)); | ||
| 243 | } | 395 | } |
| 244 | 396 | ||
| 245 | /* set the pointer to the string */ | 397 | /* set the pointer to the string */ |
| @@ -251,9 +403,6 @@ static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) | |||
| 251 | } | 403 | } |
| 252 | buf[i] = '\0'; | 404 | buf[i] = '\0'; |
| 253 | 405 | ||
| 254 | /* calculate the string length using pointer difference */ | ||
| 255 | cmd_output->lens[lineno] = (size_t)&buf[i] - (size_t)cmd_output->line[lineno]; | ||
| 256 | |||
| 257 | lineno++; | 406 | lineno++; |
| 258 | i++; | 407 | i++; |
| 259 | } | 408 | } |
| @@ -336,6 +485,139 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { | |||
| 336 | return cmd_run_array(argv, out, err, flags); | 485 | return cmd_run_array(argv, out, err, flags); |
| 337 | } | 486 | } |
| 338 | 487 | ||
| 488 | cmd_run_result cmd_run2(const char *cmd_string, int flags) { | ||
| 489 | cmd_run_result result = { | ||
| 490 | .cmd_error_code = 0, | ||
| 491 | .error_code = 0, | ||
| 492 | .stderr = | ||
| 493 | { | ||
| 494 | .buf = NULL, | ||
| 495 | .buflen = 0, | ||
| 496 | .line = NULL, | ||
| 497 | .lines = 0, | ||
| 498 | }, | ||
| 499 | .stdout = | ||
| 500 | { | ||
| 501 | .buf = NULL, | ||
| 502 | .buflen = 0, | ||
| 503 | .line = NULL, | ||
| 504 | .lines = 0, | ||
| 505 | }, | ||
| 506 | }; | ||
| 507 | |||
| 508 | if (cmd_string == NULL) { | ||
| 509 | result.error_code = -1; | ||
| 510 | return result; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* make copy of command string so strtok() doesn't silently modify it */ | ||
| 514 | /* (the calling program may want to access it later) */ | ||
| 515 | char *cmd = strdup(cmd_string); | ||
| 516 | if (cmd == NULL) { | ||
| 517 | result.error_code = -1; | ||
| 518 | return result; | ||
| 519 | } | ||
| 520 | |||
| 521 | /* This is not a shell, so we don't handle "???" */ | ||
| 522 | if (strstr(cmd, "\"")) { | ||
| 523 | result.error_code = -1; | ||
| 524 | return result; | ||
| 525 | } | ||
| 526 | |||
| 527 | /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ | ||
| 528 | if (strstr(cmd, " ' ") || strstr(cmd, "'''")) { | ||
| 529 | result.error_code = -1; | ||
| 530 | return result; | ||
| 531 | } | ||
| 532 | |||
| 533 | /* each arg must be whitespace-separated, so args can be a maximum | ||
| 534 | * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ | ||
| 535 | size_t cmdlen = strlen(cmd_string); | ||
| 536 | size_t argc = (cmdlen >> 1) + 2; | ||
| 537 | char **argv = calloc(argc, sizeof(char *)); | ||
| 538 | |||
| 539 | if (argv == NULL) { | ||
| 540 | printf("%s\n", _("Could not malloc argv array in popen()")); | ||
| 541 | result.error_code = -1; | ||
| 542 | return result; | ||
| 543 | } | ||
| 544 | |||
| 545 | /* get command arguments (stupidly, but fairly quickly) */ | ||
| 546 | for (int i = 0; cmd; i++) { | ||
| 547 | char *str = cmd; | ||
| 548 | str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ | ||
| 549 | |||
| 550 | if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ | ||
| 551 | str++; | ||
| 552 | if (!strstr(str, "'")) { | ||
| 553 | result.error_code = -1; | ||
| 554 | return result; /* balanced? */ | ||
| 555 | } | ||
| 556 | |||
| 557 | cmd = 1 + strstr(str, "'"); | ||
| 558 | str[strcspn(str, "'")] = 0; | ||
| 559 | } else { | ||
| 560 | if (strpbrk(str, " \t\r\n")) { | ||
| 561 | cmd = 1 + strpbrk(str, " \t\r\n"); | ||
| 562 | str[strcspn(str, " \t\r\n")] = 0; | ||
| 563 | } else { | ||
| 564 | cmd = NULL; | ||
| 565 | } | ||
| 566 | } | ||
| 567 | |||
| 568 | if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { | ||
| 569 | cmd = NULL; | ||
| 570 | } | ||
| 571 | |||
| 572 | argv[i++] = str; | ||
| 573 | } | ||
| 574 | |||
| 575 | result = cmd_run_array2(argv, flags); | ||
| 576 | |||
| 577 | return result; | ||
| 578 | } | ||
| 579 | |||
| 580 | cmd_run_result cmd_run_array2(char *const *cmd, int flags) { | ||
| 581 | cmd_run_result result = { | ||
| 582 | .cmd_error_code = 0, | ||
| 583 | .error_code = 0, | ||
| 584 | .stderr = | ||
| 585 | { | ||
| 586 | .buf = NULL, | ||
| 587 | .buflen = 0, | ||
| 588 | .line = NULL, | ||
| 589 | .lines = 0, | ||
| 590 | }, | ||
| 591 | .stdout = | ||
| 592 | { | ||
| 593 | .buf = NULL, | ||
| 594 | .buflen = 0, | ||
| 595 | .line = NULL, | ||
| 596 | .lines = 0, | ||
| 597 | }, | ||
| 598 | }; | ||
| 599 | |||
| 600 | int_cmd_open_result cmd_open_result = _cmd_open2(cmd); | ||
| 601 | if (cmd_open_result.error_code != 0) { | ||
| 602 | // result.error_code = -1; | ||
| 603 | // return result; | ||
| 604 | // TODO properly handle this without dying | ||
| 605 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd[0]); | ||
| 606 | } | ||
| 607 | |||
| 608 | int file_descriptor = cmd_open_result.file_descriptor; | ||
| 609 | int pfd_out[2] = {cmd_open_result.stdout_pipe_fd[0], cmd_open_result.stdout_pipe_fd[1]}; | ||
| 610 | int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]}; | ||
| 611 | |||
| 612 | int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags); | ||
| 613 | result.stdout = tmp_stdout.output_container; | ||
| 614 | int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags); | ||
| 615 | result.stderr = tmp_stderr.output_container; | ||
| 616 | |||
| 617 | result.cmd_error_code = _cmd_close(file_descriptor); | ||
| 618 | return result; | ||
| 619 | } | ||
| 620 | |||
| 339 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { | 621 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { |
| 340 | /* initialize the structs */ | 622 | /* initialize the structs */ |
| 341 | if (out) { | 623 | if (out) { |
diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index 3672cdc9..d3a8f14f 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h | |||
| @@ -13,7 +13,6 @@ typedef struct { | |||
| 13 | char *buf; /* output buffer */ | 13 | char *buf; /* output buffer */ |
| 14 | size_t buflen; /* output buffer content length */ | 14 | size_t buflen; /* output buffer content length */ |
| 15 | char **line; /* array of lines (points to buf) */ | 15 | char **line; /* array of lines (points to buf) */ |
| 16 | size_t *lens; /* string lengths */ | ||
| 17 | size_t lines; /* lines of output */ | 16 | size_t lines; /* lines of output */ |
| 18 | } output; | 17 | } output; |
| 19 | 18 | ||
| @@ -22,6 +21,15 @@ int cmd_run(const char *, output *, output *, int); | |||
| 22 | int cmd_run_array(char *const *, output *, output *, int); | 21 | int cmd_run_array(char *const *, output *, output *, int); |
| 23 | int cmd_file_read(const char *, output *, int); | 22 | int cmd_file_read(const char *, output *, int); |
| 24 | 23 | ||
| 24 | typedef struct { | ||
| 25 | int error_code; | ||
| 26 | int cmd_error_code; | ||
| 27 | output stdout; | ||
| 28 | output stderr; | ||
| 29 | } cmd_run_result; | ||
| 30 | cmd_run_result cmd_run2(const char *cmd, int flags); | ||
| 31 | cmd_run_result cmd_run_array2(char * const *cmd, int flags); | ||
| 32 | |||
| 25 | /* only multi-threaded plugins need to bother with this */ | 33 | /* only multi-threaded plugins need to bother with this */ |
| 26 | void cmd_init(void); | 34 | void cmd_init(void); |
| 27 | #define CMD_INIT cmd_init() | 35 | #define CMD_INIT cmd_init() |
