1 /*
2 * A simple interface to executing programs from other programs, using an
3 * optimized and safer popen()-like implementation. It is considered safer in
4 * that no shell needs to be spawned for simple commands, and the environment
5 * passed to the execve()'d program is essentially empty.
6 *
7 * This code is based on popen.c, which in turn was taken from
8 * "Advanced Programming in the UNIX Environment" by W. Richard Stevens.
9 *
10 * Care has been taken to make sure the functions are async-safe. The exception
11 * is runcmd_init() which multithreaded applications or plugins must call in a
12 * non-reentrant manner before calling any other runcmd function.
13 */
14
15 #define NAGIOSPLUG_API_C 1
16
17 /* includes **/
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <sys/wait.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <errno.h>
26 #include "runcmd.h"
27
28
29 /** macros **/
30 #ifndef WEXITSTATUS
31 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
32 #endif
33
34 #ifndef WIFEXITED
35 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
36 #endif
37
38 /* Determine whether we have setenv()/unsetenv() (see setenv(3) on Linux) */
39 #if defined(__DragonFly__) || __FreeBSD__ || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
40 # define HAVE_SETENV
41 #endif
42
43 /*
44 * This variable must be global, since there's no way the caller
45 * can forcibly slay a dead or ungainly running program otherwise.
46 *
47 * The check for initialized values and allocation is not atomic, and can
48 * potentially occur in any number of threads simultaneously.
49 *
50 * Multithreaded apps and plugins must initialize it (via runcmd_init())
51 * in an async safe manner before calling any other runcmd function.
52 */
53 static pid_t *pids = NULL;
54
55 /* If OPEN_MAX isn't defined, we try the sysconf syscall first.
56 * If that fails, we fall back to an educated guess which is accurate
57 * on Linux and some other systems. There's no guarantee that our guess is
58 * adequate and the program will die with SIGSEGV if it isn't and the
59 * upper boundary is breached. */
60 #ifdef OPEN_MAX
61 # define maxfd OPEN_MAX
62 #else
63 # ifndef _SC_OPEN_MAX /* sysconf macro unavailable, so guess */
64 # define maxfd 256
65 # else
66 static int maxfd = 0;
67 # endif /* _SC_OPEN_MAX */
68 #endif /* OPEN_MAX */
69
70
runcmd_strerror(int code)71 const char *runcmd_strerror(int code)
72 {
73 switch (code) {
74 case RUNCMD_EFD:
75 return "pipe() or open() failed";
76 case RUNCMD_EALLOC:
77 return "memory allocation failed";
78 case RUNCMD_ECMD:
79 return "command too complicated";
80 case RUNCMD_EFORK:
81 return "failed to fork()";
82 case RUNCMD_EINVAL:
83 return "invalid parameters";
84 case RUNCMD_EWAIT:
85 return "wait() failed";
86 }
87 return "unknown";
88 }
89
90 /* yield the pid belonging to a particular file descriptor */
runcmd_pid(int fd)91 pid_t runcmd_pid(int fd)
92 {
93 if(!pids || fd >= maxfd || fd < 0)
94 return 0;
95
96 return pids[fd];
97 }
98
99 /*
100 * Simple command parser which is still tolerably accurate for our
101 * simple needs. It might serve as a useful example on how to program
102 * a state-machine though.
103 *
104 * It's up to the caller to handle output redirection, job control,
105 * conditional statements, variable substitution, nested commands and
106 * function execution. We do mark such occasions with the return code
107 * though, which is to be interpreted as a bitfield with potentially
108 * multiple flags set.
109 */
110 #define STATE_NONE 0
111 #define STATE_WHITE (1 << 0)
112 #define STATE_INARG (1 << 1)
113 #define STATE_INSQ (1 << 2)
114 #define STATE_INDQ (1 << 3)
115 #define STATE_SPECIAL (1 << 4)
116 #define STATE_BSLASH (1 << 5)
117 #define in_quotes (state & (STATE_INSQ | STATE_INDQ))
118 #define is_state(s) (state == s)
119 #define set_state(s) (state = s)
120 #define have_state(s) ((state & s) == s)
121 #define add_state(s) (state |= s)
122 #define del_state(s) (state &= ~s)
123 #define add_ret(r) (ret |= r)
runcmd_cmd2strv(const char * str,int * out_argc,char ** out_argv)124 int runcmd_cmd2strv(const char *str, int *out_argc, char **out_argv)
125 {
126 int arg = 0;
127 int a = 0;
128 unsigned int i;
129 int state;
130 int ret = 0;
131 size_t len;
132 char *argz;
133
134 set_state(STATE_NONE);
135
136 if (!str || !*str || !out_argc || !out_argv)
137 return RUNCMD_EINVAL;
138
139 len = strlen(str);
140 argz = malloc(len + 1);
141 if (!argz)
142 return RUNCMD_EALLOC;
143
144 /* Point argv[0] at the parsed argument string argz so we don't leak. */
145 out_argv[0] = argz;
146 out_argv[1] = NULL;
147
148 for (i = 0; i < len; i++) {
149 const char *p = &str[i];
150
151 switch (*p) {
152 case 0:
153 if (arg == 0) free(out_argv[0]);
154 out_argv[arg] = NULL;
155 *out_argc = arg;
156 return ret;
157
158 case ' ': case '\t': case '\r': case '\n':
159 if (is_state(STATE_INARG)) {
160 set_state(STATE_NONE);
161 argz[a++] = 0;
162 continue;
163 }
164 if (!in_quotes)
165 continue;
166
167 break;
168
169 case '\\':
170 /* single-quoted strings never interpolate backslashes */
171 if (have_state(STATE_INSQ) || have_state(STATE_BSLASH)) {
172 break;
173 }
174 /*
175 * double-quoted strings let backslashes escape
176 * a few, but not all, shell specials
177 */
178 if (have_state(STATE_INDQ)) {
179 const char next = str[i + 1];
180 switch (next) {
181 case '"': case '\\': case '$': case '`':
182 add_state(STATE_BSLASH);
183 continue;
184 }
185 break;
186 }
187 /*
188 * unquoted strings remove unescaped backslashes,
189 * but backslashes escape anything and everything
190 */
191 i++;
192 break;
193
194 case '\'':
195 if (have_state(STATE_INDQ))
196 break;
197 if (have_state(STATE_INSQ)) {
198 del_state(STATE_INSQ);
199 continue;
200 }
201
202 /*
203 * quotes can come inside arguments or
204 * at the start of them
205 */
206 if (is_state(STATE_NONE) || is_state(STATE_INARG)) {
207 if (is_state(STATE_NONE)) {
208 /* starting a new argument */
209 out_argv[arg++] = &argz[a];
210 }
211 set_state(STATE_INSQ | STATE_INARG);
212 continue;
213 }
214 case '"':
215 if (have_state(STATE_INSQ))
216 break;
217 if (have_state(STATE_INDQ)) {
218 del_state(STATE_INDQ);
219 continue;
220 }
221 if (is_state(STATE_NONE) || is_state(STATE_INARG)) {
222 if (is_state(STATE_NONE)) {
223 out_argv[arg++] = &argz[a];
224 }
225 set_state(STATE_INDQ | STATE_INARG);
226 continue;
227 }
228 break;
229
230 case '|':
231 case '<':
232 case '>':
233 if (!in_quotes) {
234 add_ret(RUNCMD_HAS_REDIR);
235 }
236 break;
237 case '&': case ';':
238 if (!in_quotes) {
239 set_state(STATE_SPECIAL);
240 add_ret(RUNCMD_HAS_JOBCONTROL);
241 }
242 break;
243
244 case '`':
245 if (!have_state(STATE_INSQ) && !have_state(STATE_BSLASH)) {
246 add_ret(RUNCMD_HAS_SUBCOMMAND);
247 }
248 break;
249
250 case '(': case ')':
251 if (!in_quotes) {
252 add_ret(RUNCMD_HAS_PAREN);
253 }
254 break;
255
256 case '$':
257 if (!have_state(STATE_INSQ) && !have_state(STATE_BSLASH)) {
258 if (p[1] == '(')
259 add_ret(RUNCMD_HAS_SUBCOMMAND);
260 else
261 add_ret(RUNCMD_HAS_SHVAR);
262 }
263 break;
264
265 case '*': case '?':
266 if (!in_quotes) {
267 add_ret(RUNCMD_HAS_WILDCARD);
268 }
269 break;
270
271 default:
272 break;
273 }
274
275 /* here, we're limited to escaped backslashes, so remove STATE_BSLASH */
276 del_state(STATE_BSLASH);
277
278 if (is_state(STATE_NONE)) {
279 set_state(STATE_INARG);
280 out_argv[arg++] = &argz[a];
281 }
282
283 /* by default we simply copy the byte */
284 argz[a++] = str[i];
285 }
286
287 /* make sure we nul-terminate the last argument */
288 argz[a++] = 0;
289
290 if (have_state(STATE_INSQ))
291 add_ret(RUNCMD_HAS_UBSQ);
292 if (have_state(STATE_INDQ))
293 add_ret(RUNCMD_HAS_UBDQ);
294
295 out_argv[arg] = NULL;
296 *out_argc = arg;
297
298 return ret;
299 }
300
301
302 /* This function is NOT async-safe. It is exported so multithreaded
303 * plugins (or other apps) can call it prior to running any commands
304 * through this API and thus achieve async-safeness throughout the API. */
runcmd_init(void)305 void runcmd_init(void)
306 {
307 #if defined(RLIMIT_NOFILE)
308 if (!maxfd) {
309 struct rlimit rlim;
310 getrlimit(RLIMIT_NOFILE, &rlim);
311 maxfd = rlim.rlim_cur;
312 }
313 #elif !defined(OPEN_MAX) && !defined(IOV_MAX) && defined(_SC_OPEN_MAX)
314 if(!maxfd) {
315 if((maxfd = sysconf(_SC_OPEN_MAX)) < 0) {
316 /* possibly log or emit a warning here, since there's no
317 * guarantee that our guess at maxfd will be adequate */
318 maxfd = 256;
319 }
320 }
321 #endif
322
323 if (!pids)
324 pids = calloc(maxfd, sizeof(pid_t));
325 }
326
327
328 static int runcmd_setenv(const char *name, const char *value);
329 int update_environment(char *name, char *value, int set);
330
331 /* Start running a command */
332 /* The definition declares nonnull arguments, so checking for these
333 arguments as null results in a compiler warning. */
runcmd_open(const char * cmd,int * pfd,int * pfderr,char ** env,void (* iobreg)(int,int,void *),void * iobregarg)334 int runcmd_open(const char *cmd, int *pfd, int *pfderr, char **env,
335 void (*iobreg)(int, int, void *), void *iobregarg)
336 {
337 char **argv = NULL;
338 int argc = 0;
339 int cmd2strv_errors;
340 size_t cmdlen;
341 pid_t pid;
342
343 int i = 0;
344
345 if (!pids) {
346 runcmd_init();
347 }
348
349 /* We can't do anything without a command, or FD arrays. */
350 if (cmd == NULL || pfd == NULL || pfderr == NULL) {
351 return RUNCMD_EINVAL;
352 }
353
354 cmdlen = strlen(cmd);
355 argv = calloc((cmdlen / 2) + 5, sizeof(char *));
356 if (!argv) {
357 return RUNCMD_EALLOC;
358 }
359
360 cmd2strv_errors = runcmd_cmd2strv(cmd, &argc, argv);
361
362 /* We couldn't allocate the parsed argument array. */
363 if (cmd2strv_errors == RUNCMD_EALLOC) {
364 free(argv);
365 return RUNCMD_EALLOC;
366 }
367
368 /* Run complex commands via the shell. */
369 if (cmd2strv_errors) {
370
371 free(argv[0]);
372 argv[0] = "/bin/sh";
373 argv[1] = "-c";
374 argv[2] = strdup(cmd);
375 if (!argv[2]) {
376 free(argv);
377 return RUNCMD_EALLOC;
378 }
379 argv[3] = NULL;
380 }
381
382 if (pipe(pfd) < 0) {
383 free(!cmd2strv_errors ? argv[0] : argv[2]);
384 free(argv);
385 return RUNCMD_EFD;
386 }
387
388 if (pipe(pfderr) < 0) {
389 free(!cmd2strv_errors ? argv[0] : argv[2]);
390 free(argv);
391 close(pfd[0]);
392 close(pfd[1]);
393 return RUNCMD_EFD;
394 }
395
396 if (iobreg) {
397 iobreg(pfd[0], pfderr[0], iobregarg);
398 }
399
400 pid = fork();
401 if (pid < 0) {
402 free(!cmd2strv_errors ? argv[0] : argv[2]);
403 free(argv);
404 close(pfd[0]);
405 close(pfd[1]);
406 close(pfderr[0]);
407 close(pfderr[1]);
408 return RUNCMD_EFORK; /* errno set by the failing function */
409 }
410
411 /* Child runs excevp() and _exit(). */
412 if (pid == 0) {
413 int exit_status = EXIT_SUCCESS; /* To preserve errno when _exit()ing. */
414
415 /* Make our children their own process group leaders so they are killable
416 * by their parent (word of the day: filicide / prolicide). */
417 if (setpgid(getpid(), getpid()) == -1) {
418 exit_status = errno;
419 fprintf(stderr, "setpgid(...) errno %d: %s\n", errno, strerror(errno));
420 goto child_error_exit;
421 }
422
423 close (pfd[0]);
424 if (pfd[1] != STDOUT_FILENO) {
425 if (dup2(pfd[1], STDOUT_FILENO) == -1) {
426 exit_status = errno;
427 fprintf(stderr, "dup2(pfd[1], STDOUT_FILENO) errno %d: %s\n", errno, strerror(errno));
428 goto child_error_exit;
429 }
430 close(pfd[1]);
431 }
432
433 close (pfderr[0]);
434 if (pfderr[1] != STDERR_FILENO) {
435 if (dup2(pfderr[1], STDERR_FILENO) == -1) {
436 exit_status = errno;
437 fprintf(stderr, "dup2(pfderr[1], STDERR_FILENO) errno %d: %s\n", errno, strerror(errno));
438 goto child_error_exit;
439 }
440 close(pfderr[1]);
441 }
442
443 /* Close all descriptors in pids[], the child shouldn't see these. */
444 for (i = 0; i < maxfd; i++) {
445 if (pids[i] > 0) {
446 close(i);
447 }
448 }
449
450 /* Export the environment. */
451 if (env) {
452 for (; env[0] && env[1]; env += 2) {
453 if (runcmd_setenv(env[0], env[1]) == -1) {
454 exit_status = errno;
455 fprintf(stderr, "runcmd_setenv(%s, ...) errno %d: %s\n",
456 env[0], errno, strerror(errno));
457 goto child_error_exit;
458 }
459 }
460 }
461
462 /* Add VAR=value arguments from simple commands to the environment. */
463 i = 0;
464 if (!cmd2strv_errors) {
465 char *ev;
466 for (; i < argc && (ev = strchr(argv[i], '=')); ++i) {
467 if (*ev) *ev++ = '\0';
468 if (runcmd_setenv(argv[i], ev) == -1) {
469 exit_status = errno;
470 fprintf(stderr, "runcmd_setenv(%s, ev) errno %d: %s\n",
471 argv[i], errno, strerror(errno));
472 goto child_error_exit;
473 }
474 }
475 if (i == argc) {
476 exit_status = EXIT_FAILURE;
477 fprintf(stderr, "No command after variables.\n");
478 goto child_error_exit;
479 }
480 }
481
482 execvp(argv[i], argv + i);
483
484 exit_status = errno;
485 fprintf(stderr, "execvp(%s, ...) failed. errno is %d: %s\n", argv[i], errno, strerror(errno));
486
487 child_error_exit:
488 /* Free argv memory before exiting so valgrind doesn't see it as a leak. */
489 free(!cmd2strv_errors ? argv[0] : argv[2]);
490 free(argv);
491 _exit(exit_status);
492 }
493
494 /* parent picks up execution here */
495 /*
496 * close child's file descriptors in our address space and
497 * release the memory we used that won't get passed to the
498 * caller.
499 */
500 close(pfd[1]);
501 close(pfderr[1]);
502 free(!cmd2strv_errors ? argv[0] : argv[2]);
503 free(argv);
504
505 /* tag our file's entry in the pid-list and return it */
506 pids[pfd[0]] = pid;
507
508 return pfd[0];
509 }
510
511
runcmd_close(int fd)512 int runcmd_close(int fd)
513 {
514 int status;
515 pid_t pid;
516
517 /* make sure this fd was opened by runcmd_open() */
518 if(fd < 0 || fd > maxfd || !pids || (pid = pids[fd]) == 0)
519 return RUNCMD_EINVAL;
520
521 pids[fd] = 0;
522 if (close(fd) == -1)
523 return -1;
524
525 /* EINTR is ok (sort of), everything else is bad */
526 while (waitpid(pid, &status, 0) < 0)
527 if (errno != EINTR)
528 return RUNCMD_EWAIT;
529
530 /* return child's termination status */
531 return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1;
532 }
533
534
runcmd_try_close(int fd,int * status,int sig)535 int runcmd_try_close(int fd, int *status, int sig)
536 {
537 pid_t pid;
538 int result;
539
540 /* make sure this fd was opened by popen() */
541 if(fd < 0 || fd > maxfd || !pids || !pids[fd])
542 return RUNCMD_EINVAL;
543
544 pid = pids[fd];
545 while((result = waitpid(pid, status, WNOHANG)) != pid) {
546 if(!result) return 0;
547 if(result == -1) {
548 switch(errno) {
549 case EINTR:
550 continue;
551 case EINVAL:
552 return -1;
553 case ECHILD:
554 if(sig) {
555 result = kill(pid, sig);
556 sig = 0;
557 continue;
558 }
559 else return -1;
560 } /* switch */
561 }
562 }
563
564 pids[fd] = 0;
565 close(fd);
566 return result;
567 }
568
569 /**
570 * Sets an environment variable.
571 * This is for runcmd_open() to set the child environment.
572 * @param naem Variable name to set.
573 * @param value Value to set.
574 * @return 0 on success, -1 on error with errno set to: EINVAL if name is NULL;
575 * or the value set by setenv() or asprintf()/putenv().
576 * @note If setenv() is unavailable (e.g. Solaris), a "name=vale" string is
577 * allocated to pass to putenv(), which is retained by the environment. This
578 * 'leaked' memory will be on the heap at program exit.
579 */
runcmd_setenv(const char * name,const char * value)580 static int runcmd_setenv(const char *name, const char *value) {
581 #ifndef HAVE_SETENV
582 char *env_string = NULL;
583 #endif
584
585 /* We won't mess with null variable names or values. */
586 if (!name || !value) {
587 errno = EINVAL;
588 return -1;
589 }
590
591 errno = 0;
592 #ifdef HAVE_SETENV
593 return setenv(name, value, 1);
594 #else
595 /* For Solaris and systems that don't have setenv().
596 * This will leak memory, but in a "controlled" way, since the memory
597 * should be freed when the child process exits. */
598 if (asprintf(&env_string, "%s=%s", name, value) == -1) return -1;
599 if (!env_string) {
600 errno = ENOMEM;
601 return -1;
602 }
603 return putenv(env_string);
604 #endif
605 }
606
607 /* sets or unsets an environment variable */
update_environment(char * name,char * value,int set)608 int update_environment(char *name, char *value, int set) {
609 #ifndef HAVE_SETENV
610 char *env_string = NULL;
611 #endif
612
613 /* we won't mess with null variable names */
614 if(name == NULL) return -1;
615
616 /* set the environment variable */
617 if(set == 1) {
618
619 #ifdef HAVE_SETENV
620 setenv(name, (value == NULL) ? "" : value, 1);
621 #else
622 /* needed for Solaris and systems that don't have setenv() */
623 /* this will leak memory, but in a "controlled" way, since lost memory should be freed when the child process exits */
624 asprintf(&env_string, "%s=%s", name, (value == NULL) ? "" : value);
625 if(env_string) putenv(env_string);
626 #endif
627 }
628 /* clear the variable */
629 else {
630 #ifdef HAVE_UNSETENV
631 unsetenv(name);
632 #endif
633 }
634
635 return 0;
636 }
637
638 /**
639 * This will free pids if non-null
640 * Useful for external applications that rely on libnagios to
641 * keep a lid on potential memory leaks
642 */
runcmd_free_pids(void)643 void runcmd_free_pids(void) {
644 if (pids)
645 free(pids);
646 }
647
648