diff options
Diffstat (limited to 'src/dexec.c')
-rw-r--r-- | src/dexec.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/dexec.c b/src/dexec.c new file mode 100644 index 0000000..dba0fc1 --- /dev/null +++ b/src/dexec.c @@ -0,0 +1,173 @@ +/* $Id: exec.c 31 2003-11-05 22:04:26Z lennart $ */ + +/* + * This file is part of libdaemon. + * + * libdaemon is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libdaemon is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libdaemon; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#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 "dlog.h" +#include "dsignal.h" + +#include "dexec.h" + +#define MAX_ARGS 100 + +int daemon_exec(const char *dir, int *ret, const char *prog, ...) { + pid_t pid; + int p[2]; + unsigned n = 0; + static char buf[256]; + int sigfd, r; + fd_set fds; + va_list ap; + + 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)); + return -1; + + } else if (pid == 0) { + char *args[MAX_ARGS]; + int i; + + if (p[1] != 1) + dup2(p[1], 1); + + if (p[1] != 2) + dup2(p[1], 2); + + 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"); + _exit(EXIT_FAILURE); + } + + umask(0022); /* Set up a sane umask */ + + if (dir && chdir(dir) < 0) { + daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir); + chdir("/"); + } + + va_start(ap, prog); + for (i = 0; i < MAX_ARGS-1; i++) + if (!(args[i] = va_arg(ap, char*))) + break; + args[i] = NULL; + va_end(ap); + + execv(prog, args); + + daemon_log(LOG_ERR, "execv(%s) failed: %s\n", prog, strerror(errno)); + + _exit(EXIT_FAILURE); + } + + close(p[1]); + + FD_ZERO(&fds); + FD_SET(p[0], &fds); + FD_SET(sigfd = daemon_signal_fd(), &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)); + 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) { + daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno)); + break; + } + + 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); + } + + waitpid(pid, &r, 0); + + close(p[0]); + + if (!WIFEXITED(r)) + return -1; + + if (ret) + *ret = WEXITSTATUS(r); + return 0; +} |