diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-06-03 16:12:42 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-06-03 16:23:15 +0300 |
commit | 2e89e237afe4b8c04afe29f7b29e00e3757ab635 (patch) | |
tree | bee5d3c0cea38724f4e9ddb66d48d0c24a422b1c | |
parent | 7df4f13f95f56907cb12425408f1280c4a568369 (diff) | |
download | direvent-2e89e237afe4b8c04afe29f7b29e00e3757ab635.tar.gz direvent-2e89e237afe4b8c04afe29f7b29e00e3757ab635.tar.bz2 |
Revamp initialization system in a cleaner way.
* src/Makefile.am: Use the proper detach-*.c source depending
on the configuration output.
* src/rdaemon.c: Remove.
* src/detach-std.c: New file.
* src/detach-bsd.c: New file.
* src/detach-darwin.c: New file.
* src/dircond.c (signal_setup): Use sigv_set_all.
(sigmain): Do not reinstall the handler.
(main): Use detach() instead of daemon().
* src/dircond.h (detach): New proto.
(NITEMS): New macro.
(sigv_set_action, sigv_set_all)
(sigv_set_tab, sigv_set_action_tab): New protos.
* src/sigv.c: New file.
* tests/waitfile.c: Remove unused variable.
* doc/dircond.8: Update.
* doc/dircond.texi: Update.
-rw-r--r-- | doc/dircond.8 | 4 | ||||
-rw-r--r-- | doc/dircond.texi | 4 | ||||
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/detach-bsd.c (renamed from src/rdaemon.c) | 29 | ||||
-rw-r--r-- | src/detach-darwin.c | 115 | ||||
-rw-r--r-- | src/detach-std.c | 83 | ||||
-rw-r--r-- | src/dircond.c | 32 | ||||
-rw-r--r-- | src/dircond.h | 15 | ||||
-rw-r--r-- | src/sigv.c | 91 | ||||
-rw-r--r-- | tests/waitfile.c | 2 |
10 files changed, 336 insertions, 48 deletions
diff --git a/doc/dircond.8 b/doc/dircond.8 index b217f14..54950e3 100644 --- a/doc/dircond.8 +++ b/doc/dircond.8 @@ -414,9 +414,7 @@ Essentially the same as .BR BSD . The main difference compared to \fBLinux\fR and \fBBSD\fR is that on \fBDarwin\fR the watchers are set after disconnecting from the -controlling terminal, which means that any errors which may happen -during this process are directed to the syslog instead of the standard -error. This is because \fBDarwin\fR lacks the +controlling terminal, because \fBDarwin\fR lacks the .BR rfork (2) call and the event queue cannot be inherited by the child process. .SH "EXIT CODE" diff --git a/doc/dircond.texi b/doc/dircond.texi index 05634ed..8173749 100644 --- a/doc/dircond.texi +++ b/doc/dircond.texi @@ -966,9 +966,7 @@ or the underlying file system was unmounted. @section Darwin (Mac OS X) Essentially the same as BSD. The main difference compared to Linux and BSD is that on Darwin the watchers are set after disconnecting from the -controlling terminal, which means that any errors which may happen -during this process are directed to the syslog instead of the standard -error. This is because Darwin lacks the @code{rfork} call and the +controlling terminal, because Darwin lacks the @code{rfork} call and the event queue cannot be inherited by the child process. @node Reporting Bugs diff --git a/src/Makefile.am b/src/Makefile.am index f63b0c6..fbccb85 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,16 +8,19 @@ dircond_SOURCES=\ event.c\ hashtab.c\ watcher.c\ - progman.c + progman.c\ + sigv.c if DIRCOND_INOTIFY - dircond_SOURCES += ev_inotify.c + dircond_SOURCES += ev_inotify.c detach-std.c endif if DIRCOND_KQUEUE dircond_SOURCES += ev_kqueue.c if DIRCOND_RFORK - dircond_SOURCES += rdaemon.c + dircond_SOURCES += detach-bsd.c +else + dircond_SOURCES += detach-darwin.c endif endif diff --git a/src/rdaemon.c b/src/detach-bsd.c index f4fa356..be2532c 100644 --- a/src/rdaemon.c +++ b/src/detach-bsd.c @@ -14,9 +14,9 @@ You should have received a copy of the GNU General Public License along with dircond. If not, see <http://www.gnu.org/licenses/>. */ -/* This file provides a replacement for the daemon(2) function to be used - on *BSD. It uses rfork instead of fork to ensure the event queue is - inherited by the child process. According to the kqueue(2) manpage: +/* "Early-init" detach() for BSD systems. It uses rfork instead of fork + to ensure the event queue is inherited by the child process. According + to the kqueue(2) manpage: The kqueue() system call creates a new kernel event queue and returns a descriptor. The queue is not inherited by a child created with fork(2). @@ -25,6 +25,7 @@ processes. */ +#include "dircond.h" #include <unistd.h> #include <fcntl.h> #include <signal.h> @@ -40,11 +41,13 @@ #endif int -daemon(int nochdir, int noclose) +detach(void (*init)()) { struct sigaction oldsa, sa; pid_t pid; int ec; + + init(); sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; @@ -72,17 +75,15 @@ daemon(int nochdir, int noclose) return -1; } - if (!nochdir) - chdir("/"); + chdir("/"); + + close(0); + close(1); + close(2); + open(_PATH_DEVNULL, O_RDONLY); + open(_PATH_DEVNULL, O_WRONLY); + dup(1); - if (!noclose) { - close(0); - close(1); - close(2); - open(_PATH_DEVNULL, O_RDONLY); - open(_PATH_DEVNULL, O_WRONLY); - dup(1); - } return 0; } diff --git a/src/detach-darwin.c b/src/detach-darwin.c new file mode 100644 index 0000000..d7209f1 --- /dev/null +++ b/src/detach-darwin.c @@ -0,0 +1,115 @@ +/* dircond - directory content watcher daemon + Copyright (C) 2012, 2013 Sergey Poznyakoff + + Dircond 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 3 of the License, or (at your + option) any later version. + + Dircond 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 dircond. If not, see <http://www.gnu.org/licenses/>. */ + +/* A "post-init" variant of detach(), used on systems where the kqueue + state cannot be inherited by the child process (most notably, Darwin). + + The function first performs a fork, then calls the init function, while + still being connected to the controlling terminal and having the first + three descriptors inherited from the parent process. If the initialization + succeeds, the child sends a SIGUSR1 to the parent, closes the first three + descriptors and disconnects itself from the controlling terminal. + Otherwise, it exits with a non-zero code. + + The parent waits until a signal is delivered. It exits successfully if + delivered a SIGUSR1, and reports an error otherwise. */ + +#include "dircond.h" +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef HAVE_PATHS_H +# include <paths.h> +#endif + +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + +static int lastsig; + +void +catch_signal(int sig) +{ + lastsig = sig; +} + +void +waitchild() +{ + while (lastsig == 0) + pause(); + + if (lastsig == SIGUSR1) + _exit(0); + diag(LOG_CRIT, "failed to install watchers"); + exit(1); +} + +int +detach(void (*init)()) +{ + static struct sigtab sigtab[] = { + { SIGHUP, SIG_IGN }, + { SIGCHLD, catch_signal }, + { SIGUSR1, catch_signal } + }; + struct sigaction oldsa[NITEMS(sigtab)]; + pid_t pid; + int ec; + + if (sigv_set_tab(NITEMS(sigtab), sigtab, oldsa)) + return -1; + + switch (fork()) { + case -1: + sigv_restore_tab(NITEMS(sigtab), sigtab, oldsa); + return -1; + case 0: + break; + default: + waitchild(); + } + + init(); + kill(getppid(), SIGUSR1); + + pid = setsid(); + ec = errno; + + sigv_restore_tab(NITEMS(sigtab), sigtab, oldsa); + + if (pid == -1) { + errno = ec; + return -1; + } + + chdir("/"); + + close(0); + close(1); + close(2); + open(_PATH_DEVNULL, O_RDONLY); + open(_PATH_DEVNULL, O_WRONLY); + dup(1); + + return 0; +} + + diff --git a/src/detach-std.c b/src/detach-std.c new file mode 100644 index 0000000..2a8368d --- /dev/null +++ b/src/detach-std.c @@ -0,0 +1,83 @@ +/* dircond - directory content watcher daemon + Copyright (C) 2012, 2013 Sergey Poznyakoff + + Dircond 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 3 of the License, or (at your + option) any later version. + + Dircond 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 dircond. If not, see <http://www.gnu.org/licenses/>. */ + +/* A standard "early-init" version of detach(). The initialization function + is called before fork. No special actions are needed to preserve the + initialized watchers' state across fork. */ + +#include "dircond.h" +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef HAVE_PATHS_H +# include <paths.h> +#endif + +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + +int +detach(void (*init)()) +{ + struct sigaction oldsa, sa; + pid_t pid; + int ec; + + init(); + + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + + if (sigaction(SIGHUP, &sa, &oldsa)) + return -1; + + switch (fork()) { + case -1: + return -1; + case 0: + break; + default: + _exit(0); + } + + pid = setsid(); + ec = errno; + + sigaction(SIGHUP, &oldsa, NULL); + + if (pid == -1) { + errno = ec; + return -1; + } + + chdir("/"); + + close(0); + close(1); + close(2); + open(_PATH_DEVNULL, O_RDONLY); + open(_PATH_DEVNULL, O_WRONLY); + dup(1); + + return 0; +} + + diff --git a/src/dircond.c b/src/dircond.c index e39ae8d..e8b2980 100644 --- a/src/dircond.c +++ b/src/dircond.c @@ -256,14 +256,9 @@ set_program_name(const char *arg) void signal_setup(void (*sf) (int)) { - signal(SIGTERM, sf); - signal(SIGQUIT, sf); - signal(SIGINT, sf); - signal(SIGHUP, sf); - signal(SIGALRM, sf); - signal(SIGUSR1, sf); - signal(SIGUSR2, sf); - signal(SIGCHLD, sf); + static int sigv[] = { SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGALRM, + SIGUSR1, SIGUSR1, SIGCHLD }; + sigv_set_all(sf, NITEMS(sigv), sigv, NULL); } void @@ -402,18 +397,13 @@ sigmain(int sig) default: stop = 1; } - signal(sig, sigmain); } #if USE_IFACE == IFACE_INOTIFY # define INTERFACE "inotify" -# define INIT_EARLY 1 #elif USE_IFACE == IFACE_KQUEUE # define INTERFACE "kqueue" -# ifdef HAVE_RFORK -# define INIT_EARLY 1 -# endif #endif static int opt_debug_level = 0; @@ -478,23 +468,17 @@ main(int argc, char **argv) grecs_log_to_stderr = 0; } -#ifdef INIT_EARLY - setup_watchers(); -#endif - - /* Become a daemon */ - if (!foreground) { - if (daemon(0, 0)) { + if (foreground) + setup_watchers(); + else { + /* Become a daemon */ + if (detach(setup_watchers)) { diag(LOG_CRIT, "daemon: %s", strerror(errno)); exit(1); } log_to_stderr = -1; } - -#ifndef INIT_EARLY - setup_watchers(); -#endif diag(LOG_INFO, "%s %s started", program_name, VERSION); /* Write pidfile */ diff --git a/src/dircond.h b/src/dircond.h index 9bac1e8..ec3b905 100644 --- a/src/dircond.h +++ b/src/dircond.h @@ -22,6 +22,7 @@ #include <errno.h> #include <string.h> #include <unistd.h> +#include <signal.h> /* System-independent event codes */ #define SIE_CREATE 0x01 @@ -103,6 +104,7 @@ void debugprt(const char *fmt, ...); #define debug(l, c) do { if (debug_level>=(l)) debugprt c; } while(0) void signal_setup(void (*sf) (int)); +int detach(void (*)(void)); int evsys_filemask(struct dirwatcher *dp); void evsys_init(void); @@ -184,3 +186,16 @@ void process_timeouts(void); int run_handler(struct handler *hp, event_mask *event, const char *dir, const char *file); char **environ_setup(char **hint, char **kve); + +#define NITEMS(a) ((sizeof(a)/sizeof((a)[0]))) +struct sigtab { + int signo; + void (*sigfun)(int); +}; + +int sigv_set_action(int sigc, int *sigv, struct sigaction *sa); +int sigv_set_all(void (*handler)(int), int sigc, int *sigv, + struct sigaction *retsa); +int sigv_set_tab(int sigc, struct sigtab *sigtab, struct sigaction *retsa); +int sigv_set_action_tab(int sigc, struct sigtab *sigtab, struct sigaction *sa); + diff --git a/src/sigv.c b/src/sigv.c new file mode 100644 index 0000000..649c81e --- /dev/null +++ b/src/sigv.c @@ -0,0 +1,91 @@ +/* dircond - directory content watcher daemon + Copyright (C) 2012, 2013 Sergey Poznyakoff + + Dircond 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 3 of the License, or (at your + option) any later version. + + Dircond 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 dircond. If not, see <http://www.gnu.org/licenses/>. */ + +#include "dircond.h" + +int +sigv_set_action(int sigc, int *sigv, struct sigaction *sa) +{ + int i; + + for (i = 0; i < sigc; i++) { + if (sigaction(sigv[i], &sa[i], NULL)) + return i+1; + } + return 0; +} + +int +sigv_restore_tab(int sigc, struct sigtab *sigtab, struct sigaction *sa) +{ + int i; + + for (i = 0; i < sigc; i++) { + if (sigaction(sigtab[i].signo, &sa[i], NULL)) + return i+1; + } + return 0; +} + +int +sigv_set_all(void (*handler)(int), int sigc, int *sigv, + struct sigaction *retsa) +{ + int i; + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + for (i = 0; i < sigc; i++) { + sa.sa_handler = handler; + + if (sigaction(sigv[i], &sa, retsa ? &retsa[i] : NULL)) { + if (retsa) { + int ec = errno; + sigv_set_action(i, sigv, retsa); + errno = ec; + } + return -1; + } + } + return 0; +} + +int +sigv_set_tab(int sigc, struct sigtab *sigtab, struct sigaction *retsa) +{ + int i; + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + for (i = 0; i < sigc; i++) { + sa.sa_handler = sigtab[i].sigfun; + + if (sigaction(sigtab[i].signo, &sa, + retsa ? &retsa[i] : NULL)) { + if (retsa) { + int ec = errno; + sigv_restore_tab(i, sigtab, retsa); + errno = ec; + } + return -1; + } + } + return 0; +} diff --git a/tests/waitfile.c b/tests/waitfile.c index 8906198..3d04936 100644 --- a/tests/waitfile.c +++ b/tests/waitfile.c @@ -29,7 +29,7 @@ main(int argc, char **argv) { int ttl; struct timeval tv; - time_t start, now; + time_t start; if (argc != 3) { fprintf(stderr, "usage: %s FILE TIMEOUT\n", argv[0]); |