aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-06-03 16:12:42 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-06-03 16:23:15 +0300
commit2e89e237afe4b8c04afe29f7b29e00e3757ab635 (patch)
treebee5d3c0cea38724f4e9ddb66d48d0c24a422b1c
parent7df4f13f95f56907cb12425408f1280c4a568369 (diff)
downloaddirevent-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.84
-rw-r--r--doc/dircond.texi4
-rw-r--r--src/Makefile.am9
-rw-r--r--src/detach-bsd.c (renamed from src/rdaemon.c)29
-rw-r--r--src/detach-darwin.c115
-rw-r--r--src/detach-std.c83
-rw-r--r--src/dircond.c32
-rw-r--r--src/dircond.h15
-rw-r--r--src/sigv.c91
-rw-r--r--tests/waitfile.c2
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]);

Return to:

Send suggestions and report system problems to the System administrator.