diff options
| author | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-11-16 14:26:41 +0100 |
|---|---|---|
| committer | Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> | 2025-11-16 14:26:41 +0100 |
| commit | 7bfb16e0da721dcf50558f9104d3ed84efe03516 (patch) | |
| tree | b04570308ac12cb1c1831c54be027a3a5cfc4d57 /lib/utils_cmd.c | |
| parent | 2510d9ad5851c669ace7cfc16ea3ff9bf2c86106 (diff) | |
| download | monitoring-plugins-7bfb16e0da721dcf50558f9104d3ed84efe03516.tar.gz | |
Implement replacement functions for executing commands
This commit implements replacement functions for the previous
exec functions.
The replacements are implemented in a more "pure" style, the do no
longer receive pointer arguments which they will write to, but create
the pointers themselves and should therefore be easier to use,
since it is more obvious what goes in and what comes out.
Also a essentialy unused variable was removed with this.
Diffstat (limited to 'lib/utils_cmd.c')
| -rw-r--r-- | lib/utils_cmd.c | 299 |
1 files changed, 290 insertions, 9 deletions
diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 35b83297..a0213f6b 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | * | 36 | * |
| 37 | *****************************************************************************/ | 37 | *****************************************************************************/ |
| 38 | 38 | ||
| 39 | #include <stddef.h> | ||
| 39 | #define NAGIOSPLUG_API_C 1 | 40 | #define NAGIOSPLUG_API_C 1 |
| 40 | 41 | ||
| 41 | /** includes **/ | 42 | /** includes **/ |
| @@ -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,138 @@ 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 | die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), cmd[0]); | ||
| 605 | } | ||
| 606 | |||
| 607 | int file_descriptor = cmd_open_result.file_descriptor; | ||
| 608 | int pfd_out[2] = {cmd_open_result.stdout_pipe_fd[0], cmd_open_result.stdout_pipe_fd[1]}; | ||
| 609 | int pfd_err[2] = {cmd_open_result.stderr_pipe_fd[0], cmd_open_result.stderr_pipe_fd[1]}; | ||
| 610 | |||
| 611 | int_cmd_fetch_output2 tmp_stdout = _cmd_fetch_output2(pfd_out[0], flags); | ||
| 612 | result.stdout = tmp_stdout.output_container; | ||
| 613 | int_cmd_fetch_output2 tmp_stderr = _cmd_fetch_output2(pfd_err[0], flags); | ||
| 614 | result.stderr = tmp_stderr.output_container; | ||
| 615 | |||
| 616 | result.cmd_error_code = _cmd_close(file_descriptor); | ||
| 617 | return result; | ||
| 618 | } | ||
| 619 | |||
| 339 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { | 620 | int cmd_run_array(char *const *argv, output *out, output *err, int flags) { |
| 340 | /* initialize the structs */ | 621 | /* initialize the structs */ |
| 341 | if (out) { | 622 | if (out) { |
