summaryrefslogtreecommitdiffstats
path: root/lib/utils_cmd.c
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-16 14:26:41 +0100
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-11-16 14:26:41 +0100
commit7bfb16e0da721dcf50558f9104d3ed84efe03516 (patch)
treeb04570308ac12cb1c1831c54be027a3a5cfc4d57 /lib/utils_cmd.c
parent2510d9ad5851c669ace7cfc16ea3ff9bf2c86106 (diff)
downloadmonitoring-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.c299
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;
79static int _cmd_open(char *const *argv, int *pfd, int *pfderr) 80static int _cmd_open(char *const *argv, int *pfd, int *pfderr)
80 __attribute__((__nonnull__(1, 2, 3))); 81 __attribute__((__nonnull__(1, 2, 3)));
81 82
82static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) __attribute__((__nonnull__(2))); 83static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags)
84 __attribute__((__nonnull__(2)));
83 85
84static int _cmd_close(int fileDescriptor); 86static int _cmd_close(int fileDescriptor);
85 87
@@ -102,14 +104,85 @@ void cmd_init(void) {
102 } 104 }
103} 105}
104 106
107typedef 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;
113static 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 */
106static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { 181static 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
268typedef struct {
269 int error_code;
270 output output_container;
271} int_cmd_fetch_output2;
272static 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
195static int _cmd_fetch_output(int fileDescriptor, output *cmd_output, int flags) { 349static 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
488cmd_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
580cmd_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
339int cmd_run_array(char *const *argv, output *out, output *err, int flags) { 620int 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) {