summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/lib/events/config.mk2
-rw-r--r--source4/lib/events/events.c12
-rw-r--r--source4/lib/events/events.h10
-rw-r--r--source4/lib/events/events_aio.c10
-rw-r--r--source4/lib/events/events_epoll.c12
-rw-r--r--source4/lib/events/events_internal.h28
-rw-r--r--source4/lib/events/events_liboop.c1
-rw-r--r--source4/lib/events/events_select.c12
-rw-r--r--source4/lib/events/events_signal.c153
-rw-r--r--source4/lib/events/events_standard.c23
10 files changed, 261 insertions, 2 deletions
diff --git a/source4/lib/events/config.mk b/source4/lib/events/config.mk
index 5830ca38f4..0a1a3bf0f9 100644
--- a/source4/lib/events/config.mk
+++ b/source4/lib/events/config.mk
@@ -31,7 +31,7 @@ INIT_FUNCTION = events_standard_init
##############################
# Start SUBSYSTEM LIBEVENTS
[SUBSYSTEM::LIBEVENTS]
-OBJ_FILES = events.o events_timed.o
+OBJ_FILES = events.o events_timed.o events_signal.o
PUBLIC_DEPENDENCIES = LIBTALLOC
# End SUBSYSTEM LIBEVENTS
##############################
diff --git a/source4/lib/events/events.c b/source4/lib/events/events.c
index 6159ea147f..38f2352f3b 100644
--- a/source4/lib/events/events.c
+++ b/source4/lib/events/events.c
@@ -240,6 +240,18 @@ struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ct
}
/*
+ add a signal event
+ return NULL on failure
+*/
+struct timed_event *event_add_signal(struct event_context *ev, TALLOC_CTX *mem_ctx,
+ int signum,
+ event_signal_handler_t handler,
+ void *private_data)
+{
+ return ev->ops->add_signal(ev, mem_ctx, signum, handler, private_data);
+}
+
+/*
do a single event loop using the events defined in ev
*/
_PUBLIC_ int event_loop_once(struct event_context *ev)
diff --git a/source4/lib/events/events.h b/source4/lib/events/events.h
index 62f1d8e9df..eee7a5e470 100644
--- a/source4/lib/events/events.h
+++ b/source4/lib/events/events.h
@@ -28,14 +28,17 @@ struct event_ops;
struct fd_event;
struct timed_event;
struct aio_event;
+struct signal_event;
/* event handler types */
typedef void (*event_fd_handler_t)(struct event_context *, struct fd_event *,
uint16_t , void *);
typedef void (*event_timed_handler_t)(struct event_context *, struct timed_event *,
struct timeval , void *);
+typedef void (*event_signal_handler_t)(struct event_context *, struct signal_event *,
+ int , int, void *);
typedef void (*event_aio_handler_t)(struct event_context *, struct aio_event *,
- int , void *);
+ int, void *);
struct event_context *event_context_init(TALLOC_CTX *mem_ctx);
struct event_context *event_context_init_byname(TALLOC_CTX *mem_ctx, const char *name);
@@ -50,6 +53,11 @@ struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ct
event_timed_handler_t handler,
void *private);
+struct signal_event *event_add_signal(struct event_context *ev, TALLOC_CTX *mem_ctx,
+ int signum,
+ event_signal_handler_t handler,
+ void *private);
+
struct iocb;
struct aio_event *event_add_aio(struct event_context *ev,
TALLOC_CTX *mem_ctx,
diff --git a/source4/lib/events/events_aio.c b/source4/lib/events/events_aio.c
index 6d9dd6d753..327b36bcba 100644
--- a/source4/lib/events/events_aio.c
+++ b/source4/lib/events/events_aio.c
@@ -225,6 +225,11 @@ static int aio_event_loop(struct aio_event_context *aio_ev, struct timeval *tval
if (aio_ev->epoll_fd == -1) return -1;
+ if (aio_ev->ev->num_signal_handlers &&
+ common_event_check_signal(aio_ev->ev)) {
+ return 0;
+ }
+
if (tvalp) {
timeout.tv_sec = tvalp->tv_sec;
timeout.tv_nsec = tvalp->tv_usec;
@@ -236,7 +241,11 @@ static int aio_event_loop(struct aio_event_context *aio_ev, struct timeval *tval
ret = io_getevents(aio_ev->ioctx, 1, 8,
events, tvalp?&timeout:NULL);
+
if (ret == -EINTR) {
+ if (aio_ev->ev->num_signal_handlers) {
+ common_event_check_signal(aio_ev->ev);
+ }
return 0;
}
@@ -488,6 +497,7 @@ static const struct event_ops aio_event_ops = {
.get_fd_flags = aio_event_get_fd_flags,
.set_fd_flags = aio_event_set_fd_flags,
.add_timed = common_event_add_timed,
+ .add_signal = common_event_add_signal,
.loop_once = aio_event_loop_once,
.loop_wait = aio_event_loop_wait,
};
diff --git a/source4/lib/events/events_epoll.c b/source4/lib/events/events_epoll.c
index f69b5bc6f4..efcbb97f0b 100644
--- a/source4/lib/events/events_epoll.c
+++ b/source4/lib/events/events_epoll.c
@@ -214,8 +214,19 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval
timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000);
}
+ if (epoll_ev->ev->num_signal_handlers &&
+ common_event_check_signal(epoll_ev->ev)) {
+ return 0;
+ }
+
ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout);
+ if (ret == -1 && errno == EINTR && epoll_ev->ev->num_signal_handlers) {
+ if (common_event_check_signal(epoll_ev->ev)) {
+ return 0;
+ }
+ }
+
if (ret == -1 && errno != EINTR) {
epoll_fallback_to_select(epoll_ev, "epoll_wait() failed");
return -1;
@@ -397,6 +408,7 @@ static const struct event_ops epoll_event_ops = {
.get_fd_flags = epoll_event_get_fd_flags,
.set_fd_flags = epoll_event_set_fd_flags,
.add_timed = common_event_add_timed,
+ .add_signal = common_event_add_signal,
.loop_once = epoll_event_loop_once,
.loop_wait = epoll_event_loop_wait,
};
diff --git a/source4/lib/events/events_internal.h b/source4/lib/events/events_internal.h
index dc321769e0..0b8de12a35 100644
--- a/source4/lib/events/events_internal.h
+++ b/source4/lib/events/events_internal.h
@@ -47,6 +47,12 @@ struct event_ops {
struct iocb *iocb,
event_aio_handler_t handler,
void *private_data);
+ /* signal functions */
+ struct signal_event *(*add_signal)(struct event_context *ev,
+ TALLOC_CTX *mem_ctx,
+ int signum,
+ event_signal_handler_t handler,
+ void *private_data);
/* loop functions */
int (*loop_once)(struct event_context *ev);
@@ -77,6 +83,14 @@ struct timed_event {
void *additional_data;
};
+struct signal_event {
+ struct signal_event *prev, *next;
+ struct event_context *event_ctx;
+ event_signal_handler_t handler;
+ void *private_data;
+ int signum;
+};
+
/* aio event is private to the aio backend */
struct aio_event;
@@ -89,6 +103,12 @@ struct event_context {
/* this is private for the events_ops implementation */
void *additional_data;
+
+ /* number of signal event handlers */
+ int num_signal_handlers;
+
+ /* pipe hack used with signal handlers */
+ struct fd_event *pipe_fde;
};
@@ -98,3 +118,11 @@ struct timed_event *common_event_add_timed(struct event_context *, TALLOC_CTX *,
struct timeval, event_timed_handler_t, void *);
void common_event_loop_timer(struct event_context *);
struct timeval common_event_loop_delay(struct event_context *);
+
+struct signal_event *common_event_add_signal(struct event_context *ev,
+ TALLOC_CTX *mem_ctx,
+ int signum,
+ event_signal_handler_t handler,
+ void *private_data);
+int common_event_check_signal(struct event_context *ev);
+
diff --git a/source4/lib/events/events_liboop.c b/source4/lib/events/events_liboop.c
index 53238662f0..96bdafd956 100644
--- a/source4/lib/events/events_liboop.c
+++ b/source4/lib/events/events_liboop.c
@@ -274,6 +274,7 @@ static const struct event_ops event_oop_ops = {
.get_fd_flags = oop_event_get_fd_flags,
.set_fd_flags = oop_event_set_fd_flags,
.add_timed = oop_event_add_timed,
+ .add_signal = common_event_add_signal,
.loop_once = oop_event_loop_once,
.loop_wait = oop_event_loop_wait,
};
diff --git a/source4/lib/events/events_select.c b/source4/lib/events/events_select.c
index 5f4ecba5ca..ea84353183 100644
--- a/source4/lib/events/events_select.c
+++ b/source4/lib/events/events_select.c
@@ -197,8 +197,19 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru
}
}
+ if (select_ev->ev->num_signal_handlers &&
+ common_event_check_signal(select_ev->ev)) {
+ return 0;
+ }
+
selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
+ if (selrtn == -1 && errno == EINTR &&
+ select_ev->ev->num_signal_handlers) {
+ common_event_check_signal(select_ev->ev);
+ return 0;
+ }
+
if (selrtn == -1 && errno == EBADF) {
/* the socket is dead! this should never
happen as the socket should have first been
@@ -279,6 +290,7 @@ static const struct event_ops select_event_ops = {
.get_fd_flags = select_event_get_fd_flags,
.set_fd_flags = select_event_set_fd_flags,
.add_timed = common_event_add_timed,
+ .add_signal = common_event_add_signal,
.loop_once = select_event_loop_once,
.loop_wait = select_event_loop_wait,
};
diff --git a/source4/lib/events/events_signal.c b/source4/lib/events/events_signal.c
new file mode 100644
index 0000000000..85b42ae4ab
--- /dev/null
+++ b/source4/lib/events/events_signal.c
@@ -0,0 +1,153 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common events code for signal events
+
+ Copyright (C) Andrew Tridgell 2007
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/events/events_internal.h"
+
+#define NUM_SIGNALS 64
+
+/*
+ the poor design of signals means that this table must be static global
+*/
+static struct {
+ struct signal_event *sig_handlers[NUM_SIGNALS];
+ uint32_t signal_count[NUM_SIGNALS];
+ uint32_t got_signal;
+ int pipe_hack[2];
+} sig_state;
+
+
+/*
+ signal handler - redirects to registered signals
+*/
+static void signal_handler(int signum)
+{
+ char c = 0;
+ sig_state.signal_count[signum]++;
+ sig_state.got_signal++;
+ /* doesn't matter if this pipe overflows */
+ write(sig_state.pipe_hack[1], &c, 1);
+}
+
+
+/*
+ destroy a signal event
+*/
+static int signal_event_destructor(struct signal_event *se)
+{
+ se->event_ctx->num_signal_handlers--;
+ DLIST_REMOVE(sig_state.sig_handlers[se->signum], se);
+ if (sig_state.sig_handlers[se->signum] == NULL) {
+ signal(se->signum, SIG_DFL);
+ }
+ return 0;
+}
+
+/*
+ this is part of the pipe hack needed to avoid the signal race condition
+*/
+static void signal_pipe_handler(struct event_context *ev, struct fd_event *fde,
+ uint16_t flags, void *private)
+{
+ char c[16];
+ /* its non-blocking, doesn't matter if we read too much */
+ read(sig_state.pipe_hack[0], c, sizeof(c));
+}
+
+/*
+ add a signal event
+ return NULL on failure (memory allocation error)
+*/
+struct signal_event *common_event_add_signal(struct event_context *ev,
+ TALLOC_CTX *mem_ctx,
+ int signum,
+ event_signal_handler_t handler,
+ void *private_data)
+{
+ struct signal_event *se;
+
+ if (signum >= NUM_SIGNALS) {
+ return NULL;
+ }
+
+ se = talloc(mem_ctx?mem_ctx:ev, struct signal_event);
+ if (se == NULL) return NULL;
+
+ se->event_ctx = ev;
+ se->handler = handler;
+ se->private_data = private_data;
+ se->signum = signum;
+
+ if (sig_state.sig_handlers[signum] == NULL) {
+ signal(signum, signal_handler);
+ }
+
+ DLIST_ADD(sig_state.sig_handlers[signum], se);
+
+ talloc_set_destructor(se, signal_event_destructor);
+
+ if (ev->pipe_fde == NULL) {
+ if (sig_state.pipe_hack[0] == 0 &&
+ sig_state.pipe_hack[1] == 0) {
+ pipe(sig_state.pipe_hack);
+ set_blocking(sig_state.pipe_hack[0], False);
+ set_blocking(sig_state.pipe_hack[1], False);
+ }
+ ev->pipe_fde = event_add_fd(ev, ev, sig_state.pipe_hack[0],
+ EVENT_FD_READ, signal_pipe_handler, NULL);
+ }
+ ev->num_signal_handlers++;
+
+ return se;
+}
+
+
+/*
+ check if a signal is pending
+ return != 0 if a signal was pending
+*/
+int common_event_check_signal(struct event_context *ev)
+{
+ int i;
+ if (sig_state.got_signal == 0) {
+ return 0;
+ }
+
+ for (i=0;i<NUM_SIGNALS+1;i++) {
+ uint32_t count = sig_state.signal_count[i];
+ if (count != 0) {
+ struct signal_event *se, *next;
+ for (se=sig_state.sig_handlers[i];se;se=next) {
+ next = se->next;
+ se->handler(ev, se, i, count, se->private_data);
+ }
+ sig_state.signal_count[i] -= count;
+ sig_state.got_signal -= count;
+ }
+ }
+
+ return 1;
+}
diff --git a/source4/lib/events/events_standard.c b/source4/lib/events/events_standard.c
index b0f5259fa7..9c470235d0 100644
--- a/source4/lib/events/events_standard.c
+++ b/source4/lib/events/events_standard.c
@@ -229,8 +229,19 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv
timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000);
}
+ if (epoll_ev->ev->num_signal_handlers &&
+ common_event_check_signal(epoll_ev->ev)) {
+ return 0;
+ }
+
ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout);
+ if (ret == -1 && errno == EINTR && epoll_ev->ev->num_signal_handlers) {
+ if (common_event_check_signal(epoll_ev->ev)) {
+ return 0;
+ }
+ }
+
if (ret == -1 && errno != EINTR) {
epoll_fallback_to_select(std_ev, "epoll_wait() failed");
return -1;
@@ -434,8 +445,19 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva
}
}
+ if (std_ev->ev->num_signal_handlers &&
+ common_event_check_signal(std_ev->ev)) {
+ return 0;
+ }
+
selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
+ if (selrtn == -1 && errno == EINTR &&
+ std_ev->ev->num_signal_handlers) {
+ common_event_check_signal(std_ev->ev);
+ return 0;
+ }
+
if (selrtn == -1 && errno == EBADF) {
/* the socket is dead! this should never
happen as the socket should have first been
@@ -520,6 +542,7 @@ static const struct event_ops std_event_ops = {
.get_fd_flags = std_event_get_fd_flags,
.set_fd_flags = std_event_set_fd_flags,
.add_timed = common_event_add_timed,
+ .add_signal = common_event_add_signal,
.loop_once = std_event_loop_once,
.loop_wait = std_event_loop_wait,
};