| /*** |
| 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; |
| } |