diff options
Diffstat (limited to 'libdaemon/dexec.c')
-rw-r--r-- | libdaemon/dexec.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/libdaemon/dexec.c b/libdaemon/dexec.c new file mode 100644 index 0000000..7a89b62 --- /dev/null +++ b/libdaemon/dexec.c @@ -0,0 +1,232 @@ +/*** + This file is part of libdaemon. + + Copyright 2003-2008 Lennart Poettering + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/wait.h> +#include <limits.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> + +#include "dlog.h" +#include "dsignal.h" +#include "dfork.h" +#include "dexec.h" + +#define MAX_ARGS 64 + +int daemon_execv(const char *dir, int *ret, const char *prog, va_list ap) { + pid_t pid; + int p[2]; + unsigned n = 0; + static char buf[256]; + int sigfd, r; + fd_set fds; + int saved_errno; + + assert(daemon_signal_fd() >= 0); + + if (pipe(p) < 0) { + daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); + return -1; + } + + if ((pid = fork()) < 0) { + daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno)); + + saved_errno = errno; + close(p[0]); + close(p[1]); + errno = saved_errno; + + return -1; + + } else if (pid == 0) { + char *args[MAX_ARGS]; + int i; + + if (p[1] != 1) + if (dup2(p[1], 1) < 0) { + daemon_log(LOG_ERR, "dup2: %s", strerror(errno)); + goto fail; + } + + if (p[1] != 2) + if (dup2(p[1], 2) < 0) { + daemon_log(LOG_ERR, "dup2: %s", strerror(errno)); + goto fail; + } + + + if (p[0] > 2) + close(p[0]); + + if (p[1] > 2) + close(p[1]); + + close(0); + + if (open("/dev/null", O_RDONLY) != 0) { + daemon_log(LOG_ERR, "Unable to open /dev/null as STDIN"); + goto fail; + } + + daemon_close_all(-1); + daemon_reset_sigs(-1); + daemon_unblock_sigs(-1); + + umask(0022); /* Set up a sane umask */ + + if (dir && chdir(dir) < 0) { + daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir); + chdir("/"); + } + + for (i = 0; i < MAX_ARGS-1; i++) + if (!(args[i] = va_arg(ap, char*))) + break; + args[i] = NULL; + + execv(prog, args); + + daemon_log(LOG_ERR, "execv(%s) failed: %s", prog, strerror(errno)); + + fail: + + _exit(EXIT_FAILURE); + } + + close(p[1]); + + FD_ZERO(&fds); + FD_SET(p[0], &fds); + sigfd = daemon_signal_fd(); + FD_SET(sigfd, &fds); + + n = 0; + + for (;;) { + fd_set qfds = fds; + + if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) { + + if (errno == EINTR) + continue; + + daemon_log(LOG_ERR, "select() failed: %s", strerror(errno)); + + saved_errno = errno; + close(p[0]); + errno = saved_errno; + return -1; + } + + if (FD_ISSET(p[0], &qfds)) { + char c; + + if (read(p[0], &c, 1) != 1) + break; + + buf[n] = c; + + if (c == '\n' || n >= sizeof(buf) - 2) { + if (c != '\n') n++; + buf[n] = 0; + + if (buf[0]) + daemon_log(LOG_INFO, "client: %s", buf); + + n = 0; + } else + n++; + } + + if (FD_ISSET(sigfd, &qfds)) { + int sig; + + if ((sig = daemon_signal_next()) < 0) { + saved_errno = errno; + close(p[0]); + errno = saved_errno; + return -1; + } + + if (sig != SIGCHLD) { + daemon_log(LOG_WARNING, "Killing child."); + kill(pid, SIGTERM); + } + } + } + + if (n > 0) { + buf[n] = 0; + daemon_log(LOG_WARNING, "client: %s", buf); + } + + close(p[0]); + + for (;;) { + if (waitpid(pid, &r, 0) < 0) { + + if (errno == EINTR) + continue; + + daemon_log(LOG_ERR, "waitpid(): %s", strerror(errno)); + return -1; + } else { + if (!WIFEXITED(r)) { + errno = ECANCELED; + return -1; + } + + if (ret) + *ret = WEXITSTATUS(r); + + return 0; + } + } +} + +int daemon_exec(const char *dir, int *ret, const char *prog, ...) { + va_list ap; + int r; + + va_start(ap, prog); + r = daemon_execv(dir, ret, prog, ap); + va_end(ap); + + return r; +} |