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