diff options
Diffstat (limited to 'libdaemon/dfork.c')
-rw-r--r-- | libdaemon/dfork.c | 730 |
1 files changed, 0 insertions, 730 deletions
diff --git a/libdaemon/dfork.c b/libdaemon/dfork.c deleted file mode 100644 index 783033f..0000000 --- a/libdaemon/dfork.c +++ /dev/null @@ -1,730 +0,0 @@ -/*** - 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 <errno.h> -#include <string.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/wait.h> -#include <assert.h> -#include <sys/ioctl.h> -#include <signal.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <dirent.h> - -#include "dfork.h" -#include "dnonblock.h" -#include "dlog.h" - -#if defined(_NSIG) /* On glibc NSIG does not count RT signals */ -# define SIGNAL_UPPER_BOUND _NSIG -#elif defined(NSIG) /* Solaris defines just this */ -# define SIGNAL_UPPER_BOUND NSIG -#else -# error "Unknown upper bound for signals" -#endif - -static int _daemon_retval_pipe[2] = { -1, -1 }; - -static int _null_open(int f, int fd) { - int fd2; - - if ((fd2 = open("/dev/null", f)) < 0) - return -1; - - if (fd2 == fd) - return fd; - - if (dup2(fd2, fd) < 0) - return -1; - - close(fd2); - return fd; -} - -static ssize_t atomic_read(int fd, void *d, size_t l) { - ssize_t t = 0; - - while (l > 0) { - ssize_t r; - - if ((r = read(fd, d, l)) <= 0) { - - if (r < 0) - return t > 0 ? t : -1; - else - return t; - } - - t += r; - d = (char*) d + r; - l -= r; - } - - return t; -} - -static ssize_t atomic_write(int fd, const void *d, size_t l) { - ssize_t t = 0; - - while (l > 0) { - ssize_t r; - - if ((r = write(fd, d, l)) <= 0) { - - if (r < 0) - return t > 0 ? t : -1; - else - return t; - } - - t += r; - d = (const char*) d + r; - l -= r; - } - - return t; -} - -static int move_fd_up(int *fd) { - assert(fd); - - while (*fd <= 2) { - if ((*fd = dup(*fd)) < 0) { - daemon_log(LOG_ERR, "dup(): %s", strerror(errno)); - return -1; - } - } - - return 0; -} - -static void sigchld(int s) { -} - -pid_t daemon_fork(void) { - pid_t pid; - int pipe_fds[2] = {-1, -1}; - struct sigaction sa_old, sa_new; - sigset_t ss_old, ss_new; - int saved_errno; - - memset(&sa_new, 0, sizeof(sa_new)); - sa_new.sa_handler = sigchld; - sa_new.sa_flags = SA_RESTART; - - if (sigemptyset(&ss_new) < 0) { - daemon_log(LOG_ERR, "sigemptyset() failed: %s", strerror(errno)); - return (pid_t) -1; - } - - if (sigaddset(&ss_new, SIGCHLD) < 0) { - daemon_log(LOG_ERR, "sigaddset() failed: %s", strerror(errno)); - return (pid_t) -1; - } - - if (sigaction(SIGCHLD, &sa_new, &sa_old) < 0) { - daemon_log(LOG_ERR, "sigaction() failed: %s", strerror(errno)); - return (pid_t) -1; - } - - if (sigprocmask(SIG_UNBLOCK, &ss_new, &ss_old) < 0) { - daemon_log(LOG_ERR, "sigprocmask() failed: %s", strerror(errno)); - - saved_errno = errno; - sigaction(SIGCHLD, &sa_old, NULL); - errno = saved_errno; - - return (pid_t) -1; - } - - if (pipe(pipe_fds) < 0) { - daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); - - saved_errno = errno; - sigaction(SIGCHLD, &sa_old, NULL); - sigprocmask(SIG_SETMASK, &ss_old, NULL); - errno = saved_errno; - - return (pid_t) -1; - } - - if ((pid = fork()) < 0) { /* First fork */ - daemon_log(LOG_ERR, "First fork() failed: %s", strerror(errno)); - - saved_errno = errno; - close(pipe_fds[0]); - close(pipe_fds[1]); - sigaction(SIGCHLD, &sa_old, NULL); - sigprocmask(SIG_SETMASK, &ss_old, NULL); - errno = saved_errno; - - return (pid_t) -1; - - } else if (pid == 0) { - pid_t dpid; - - /* First child. Now we are sure not to be a session leader or - * process group leader anymore, i.e. we know that setsid() - * will succeed. */ - - if (daemon_log_use & DAEMON_LOG_AUTO) - daemon_log_use = DAEMON_LOG_SYSLOG; - - if (close(pipe_fds[0]) < 0) { - daemon_log(LOG_ERR, "close() failed: %s", strerror(errno)); - goto fail; - } - - /* Move file descriptors up*/ - if (move_fd_up(&pipe_fds[1]) < 0) - goto fail; - - if (_daemon_retval_pipe[0] >= 0 && move_fd_up(&_daemon_retval_pipe[0]) < 0) - goto fail; - if (_daemon_retval_pipe[1] >= 0 && move_fd_up(&_daemon_retval_pipe[1]) < 0) - goto fail; - - if (_null_open(O_RDONLY, 0) < 0) { - daemon_log(LOG_ERR, "Failed to open /dev/null for STDIN: %s", strerror(errno)); - goto fail; - } - - if (_null_open(O_WRONLY, 1) < 0) { - daemon_log(LOG_ERR, "Failed to open /dev/null for STDOUT: %s", strerror(errno)); - goto fail; - } - - if (_null_open(O_WRONLY, 2) < 0) { - daemon_log(LOG_ERR, "Failed to open /dev/null for STDERR: %s", strerror(errno)); - goto fail; - } - - /* Create a new session. This will create a new session and a - * new process group for us and we will be the ledaer of - * both. This should always succeed because we cannot be the - * process group leader because we just forked. */ - if (setsid() < 0) { - daemon_log(LOG_ERR, "setsid() failed: %s", strerror(errno)); - goto fail; - } - - umask(0077); - - if (chdir("/") < 0) { - daemon_log(LOG_ERR, "chdir() failed: %s", strerror(errno)); - goto fail; - } - - if ((pid = fork()) < 0) { /* Second fork */ - daemon_log(LOG_ERR, "Second fork() failed: %s", strerror(errno)); - goto fail; - - } else if (pid == 0) { - /* Second child. Our father will exit right-away. That way - * we can be sure that we are a child of init now, even if - * the process which spawned us stays around for a longer - * time. Also, since we are no session leader anymore we - * can be sure that we will never acquire a controlling - * TTY. */ - - if (sigaction(SIGCHLD, &sa_old, NULL) < 0) { - daemon_log(LOG_ERR, "close() failed: %s", strerror(errno)); - goto fail; - } - - if (sigprocmask(SIG_SETMASK, &ss_old, NULL) < 0) { - daemon_log(LOG_ERR, "sigprocmask() failed: %s", strerror(errno)); - goto fail; - } - - if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) { - daemon_log(LOG_ERR, "signal(SIGTTOU, SIG_IGN) failed: %s", strerror(errno)); - goto fail; - } - - if (signal(SIGTTIN, SIG_IGN) == SIG_ERR) { - daemon_log(LOG_ERR, "signal(SIGTTIN, SIG_IGN) failed: %s", strerror(errno)); - goto fail; - } - - if (signal(SIGTSTP, SIG_IGN) == SIG_ERR) { - daemon_log(LOG_ERR, "signal(SIGTSTP, SIG_IGN) failed: %s", strerror(errno)); - goto fail; - } - - dpid = getpid(); - if (atomic_write(pipe_fds[1], &dpid, sizeof(dpid)) != sizeof(dpid)) { - daemon_log(LOG_ERR, "write() failed: %s", strerror(errno)); - goto fail; - } - - if (close(pipe_fds[1]) < 0) { - daemon_log(LOG_ERR, "close() failed: %s", strerror(errno)); - goto fail; - } - - return 0; - - } else { - /* Second father */ - close(pipe_fds[1]); - _exit(0); - } - - fail: - dpid = (pid_t) -1; - - if (atomic_write(pipe_fds[1], &dpid, sizeof(dpid)) != sizeof(dpid)) - daemon_log(LOG_ERR, "Failed to write error PID: %s", strerror(errno)); - - close(pipe_fds[1]); - _exit(0); - - } else { - /* First father */ - pid_t dpid; - - close(pipe_fds[1]); - - if (waitpid(pid, NULL, WUNTRACED) < 0) { - saved_errno = errno; - close(pipe_fds[0]); - sigaction(SIGCHLD, &sa_old, NULL); - sigprocmask(SIG_SETMASK, &ss_old, NULL); - errno = saved_errno; - return -1; - } - - sigprocmask(SIG_SETMASK, &ss_old, NULL); - sigaction(SIGCHLD, &sa_old, NULL); - - if (atomic_read(pipe_fds[0], &dpid, sizeof(dpid)) != sizeof(dpid)) { - daemon_log(LOG_ERR, "Failed to read daemon PID."); - dpid = (pid_t) -1; - errno = EINVAL; - } else if (dpid == (pid_t) -1) - errno = EIO; - - saved_errno = errno; - close(pipe_fds[0]); - errno = saved_errno; - - return dpid; - } -} - -int daemon_retval_init(void) { - - if (_daemon_retval_pipe[0] < 0 || _daemon_retval_pipe[1] < 0) { - - if (pipe(_daemon_retval_pipe) < 0) { - daemon_log(LOG_ERR, "pipe(): %s", strerror(errno)); - return -1; - } - } - - return 0; -} - -void daemon_retval_done(void) { - int saved_errno = errno; - - if (_daemon_retval_pipe[0] >= 0) - close(_daemon_retval_pipe[0]); - - if (_daemon_retval_pipe[1] >= 0) - close(_daemon_retval_pipe[1]); - - _daemon_retval_pipe[0] = _daemon_retval_pipe[1] = -1; - - errno = saved_errno; -} - -int daemon_retval_send(int i) { - ssize_t r; - - if (_daemon_retval_pipe[1] < 0) { - errno = EINVAL; - return -1; - } - - r = atomic_write(_daemon_retval_pipe[1], &i, sizeof(i)); - - daemon_retval_done(); - - if (r != sizeof(i)) { - - if (r < 0) - daemon_log(LOG_ERR, "write() failed while writing return value to pipe: %s", strerror(errno)); - else { - daemon_log(LOG_ERR, "write() too short while writing return value from pipe"); - errno = EINVAL; - } - - return -1; - } - - return 0; -} - -int daemon_retval_wait(int timeout) { - ssize_t r; - int i; - - if (timeout > 0) { - struct timeval tv; - int s; - fd_set fds; - - tv.tv_sec = timeout; - tv.tv_usec = 0; - - FD_ZERO(&fds); - FD_SET(_daemon_retval_pipe[0], &fds); - - if ((s = select(FD_SETSIZE, &fds, 0, 0, &tv)) != 1) { - - if (s < 0) - daemon_log(LOG_ERR, "select() failed while waiting for return value: %s", strerror(errno)); - else { - errno = ETIMEDOUT; - daemon_log(LOG_ERR, "Timeout reached while wating for return value"); - } - - return -1; - } - } - - if ((r = atomic_read(_daemon_retval_pipe[0], &i, sizeof(i))) != sizeof(i)) { - - if (r < 0) - daemon_log(LOG_ERR, "read() failed while reading return value from pipe: %s", strerror(errno)); - else if (r == 0) { - daemon_log(LOG_ERR, "read() failed with EOF while reading return value from pipe."); - errno = EINVAL; - } else if (r > 0) { - daemon_log(LOG_ERR, "read() too short while reading return value from pipe."); - errno = EINVAL; - } - - return -1; - } - - daemon_retval_done(); - - return i; -} - -int daemon_close_all(int except_fd, ...) { - va_list ap; - int n = 0, i, r; - int *p; - int saved_errno; - - va_start(ap, except_fd); - - if (except_fd >= 0) - for (n = 1; va_arg(ap, int) >= 0; n++) - ; - - va_end(ap); - - if (!(p = malloc(sizeof(int) * (n+1)))) - return -1; - - va_start(ap, except_fd); - - i = 0; - if (except_fd >= 0) { - int fd; - p[i++] = except_fd; - - while ((fd = va_arg(ap, int)) >= 0) - p[i++] = fd; - } - p[i] = -1; - - va_end(ap); - - r = daemon_close_allv(p); - - saved_errno = errno; - free(p); - errno = saved_errno; - - return r; -} - -/** Same as daemon_close_all but takes an array of fds, terminated by -1 */ -int daemon_close_allv(const int except_fds[]) { - struct rlimit rl; - int fd, maxfd; - -#ifdef __linux__ - int saved_errno; - - DIR *d; - - if ((d = opendir("/proc/self/fd"))) { - - struct dirent *de; - - while ((de = readdir(d))) { - int found; - long l; - char *e = NULL; - int i; - - if (de->d_name[0] == '.') - continue; - - errno = 0; - l = strtol(de->d_name, &e, 10); - if (errno != 0 || !e || *e) { - closedir(d); - errno = EINVAL; - return -1; - } - - fd = (int) l; - - if ((long) fd != l) { - closedir(d); - errno = EINVAL; - return -1; - } - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - if (fd == _daemon_retval_pipe[1]) - continue; - - found = 0; - for (i = 0; except_fds[i] >= 0; i++) - if (except_fds[i] == fd) { - found = 1; - break; - } - - if (found) - continue; - - if (close(fd) < 0) { - saved_errno = errno; - closedir(d); - errno = saved_errno; - - return -1; - } - - if (fd == _daemon_retval_pipe[0]) - _daemon_retval_pipe[0] = -1; /* mark as closed */ - } - - closedir(d); - return 0; - } - -#endif - - if (getrlimit(RLIMIT_NOFILE, &rl) > 0) - maxfd = (int) rl.rlim_max; - else - maxfd = sysconf(_SC_OPEN_MAX); - - for (fd = 3; fd < maxfd; fd++) { - int i, found; - - if (fd == _daemon_retval_pipe[1]) - continue; - - found = 0; - for (i = 0; except_fds[i] >= 0; i++) - if (except_fds[i] == fd) { - found = 1; - break; - } - - if (found) - continue; - - if (close(fd) < 0 && errno != EBADF) - return -1; - - if (fd == _daemon_retval_pipe[0]) - _daemon_retval_pipe[0] = -1; /* mark as closed */ - } - - return 0; -} - -int daemon_unblock_sigs(int except, ...) { - va_list ap; - int n = 0, i, r; - int *p; - int saved_errno; - - va_start(ap, except); - - if (except >= 1) - for (n = 1; va_arg(ap, int) >= 0; n++) - ; - - va_end(ap); - - if (!(p = malloc(sizeof(int) * (n+1)))) - return -1; - - va_start(ap, except); - - i = 0; - if (except >= 1) { - int sig; - p[i++] = except; - - while ((sig = va_arg(ap, int)) >= 0) - p[i++] = sig; - } - p[i] = -1; - - va_end(ap); - - r = daemon_unblock_sigsv(p); - - saved_errno = errno; - free(p); - errno = saved_errno; - - return r; -} - -int daemon_unblock_sigsv(const int except[]) { - int i; - sigset_t ss; - - if (sigemptyset(&ss) < 0) - return -1; - - for (i = 0; except[i] > 0; i++) - if (sigaddset(&ss, except[i]) < 0) - return -1; - - return sigprocmask(SIG_SETMASK, &ss, NULL); -} - -int daemon_reset_sigs(int except, ...) { - va_list ap; - int n = 0, i, r; - int *p; - int saved_errno; - - va_start(ap, except); - - if (except >= 1) - for (n = 1; va_arg(ap, int) >= 0; n++) - ; - - va_end(ap); - - if (!(p = malloc(sizeof(int) * (n+1)))) - return -1; - - va_start(ap, except); - - i = 0; - if (except >= 1) { - int sig; - p[i++] = except; - - while ((sig = va_arg(ap, int)) >= 0) - p[i++] = sig; - } - p[i] = -1; - - va_end(ap); - - r = daemon_reset_sigsv(p); - - saved_errno = errno; - free(p); - errno = saved_errno; - - return r; -} - -int daemon_reset_sigsv(const int except[]) { - int sig; - - for (sig = 1; sig < SIGNAL_UPPER_BOUND; sig++) { - int reset = 1; - - switch (sig) { - case SIGKILL: - case SIGSTOP: - reset = 0; - break; - - default: { - int i; - - for (i = 0; except[i] > 0; i++) { - if (sig == except[i]) { - reset = 0; - break; - } - } - } - } - - if (reset) { - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - - /* On Linux the first two RT signals are reserved by - * glibc, and sigaction() will return EINVAL for them. */ - if ((sigaction(sig, &sa, NULL) < 0)) - if (errno != EINVAL) - return -1; - } - } - - return 0; -} |