summaryrefslogtreecommitdiffstats
path: root/lib/utils_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils_cmd.c')
-rw-r--r--lib/utils_cmd.c300
1 files changed, 291 insertions, 9 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;
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,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
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 // 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
339int cmd_run_array(char *const *argv, output *out, output *err, int flags) { 621int 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) {