summaryrefslogtreecommitdiff
path: root/libdaemon/dexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdaemon/dexec.c')
-rw-r--r--libdaemon/dexec.c232
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;
+}