summaryrefslogtreecommitdiffstats
path: root/tevent_wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tevent_wrapper.c569
1 files changed, 569 insertions, 0 deletions
diff --git a/tevent_wrapper.c b/tevent_wrapper.c
new file mode 100644
index 0000000..3870d4f
--- /dev/null
+++ b/tevent_wrapper.c
@@ -0,0 +1,569 @@
+/*
+ Infrastructure for event context wrappers
+
+ Copyright (C) Stefan Metzmacher 2014
+
+ ** NOTE! The following LGPL license applies to the tevent
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#ifdef HAVE_PTHREAD
+#include "system/threads.h"
+#endif
+#define TEVENT_DEPRECATED 1
+#include "tevent.h"
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+static int tevent_wrapper_glue_context_init(struct tevent_context *ev)
+{
+ tevent_abort(ev, "tevent_wrapper_glue_context_init() called");
+ errno = ENOSYS;
+ return -1;
+}
+
+static struct tevent_fd *tevent_wrapper_glue_add_fd(struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ int fd, uint16_t flags,
+ tevent_fd_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+ struct tevent_fd *fde = NULL;
+
+ if (glue->destroyed) {
+ tevent_abort(ev, "add_fd wrapper use after free");
+ return NULL;
+ }
+
+ if (glue->main_ev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ fde = _tevent_add_fd(glue->main_ev, mem_ctx, fd, flags,
+ handler, private_data,
+ handler_name, location);
+ if (fde == NULL) {
+ return NULL;
+ }
+
+ fde->wrapper = glue;
+
+ return fde;
+}
+
+static struct tevent_timer *tevent_wrapper_glue_add_timer(struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ struct timeval next_event,
+ tevent_timer_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+ struct tevent_timer *te = NULL;
+
+ if (glue->destroyed) {
+ tevent_abort(ev, "add_timer wrapper use after free");
+ return NULL;
+ }
+
+ if (glue->main_ev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ te = _tevent_add_timer(glue->main_ev, mem_ctx, next_event,
+ handler, private_data,
+ handler_name, location);
+ if (te == NULL) {
+ return NULL;
+ }
+
+ te->wrapper = glue;
+
+ return te;
+}
+
+static void tevent_wrapper_glue_schedule_immediate(struct tevent_immediate *im,
+ struct tevent_context *ev,
+ tevent_immediate_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+
+ if (glue->destroyed) {
+ tevent_abort(ev, "scheduke_immediate wrapper use after free");
+ return;
+ }
+
+ if (glue->main_ev == NULL) {
+ tevent_abort(ev, location);
+ errno = EINVAL;
+ return;
+ }
+
+ _tevent_schedule_immediate(im, glue->main_ev,
+ handler, private_data,
+ handler_name, location);
+
+ im->wrapper = glue;
+
+ return;
+}
+
+static struct tevent_signal *tevent_wrapper_glue_add_signal(struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ int signum, int sa_flags,
+ tevent_signal_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+ struct tevent_signal *se = NULL;
+
+ if (glue->destroyed) {
+ tevent_abort(ev, "add_signal wrapper use after free");
+ return NULL;
+ }
+
+ if (glue->main_ev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ se = _tevent_add_signal(glue->main_ev, mem_ctx,
+ signum, sa_flags,
+ handler, private_data,
+ handler_name, location);
+ if (se == NULL) {
+ return NULL;
+ }
+
+ se->wrapper = glue;
+
+ return se;
+}
+
+static int tevent_wrapper_glue_loop_once(struct tevent_context *ev, const char *location)
+{
+ tevent_abort(ev, "tevent_wrapper_glue_loop_once() called");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int tevent_wrapper_glue_loop_wait(struct tevent_context *ev, const char *location)
+{
+ tevent_abort(ev, "tevent_wrapper_glue_loop_wait() called");
+ errno = ENOSYS;
+ return -1;
+}
+
+static const struct tevent_ops tevent_wrapper_glue_ops = {
+ .context_init = tevent_wrapper_glue_context_init,
+ .add_fd = tevent_wrapper_glue_add_fd,
+ .set_fd_close_fn = tevent_common_fd_set_close_fn,
+ .get_fd_flags = tevent_common_fd_get_flags,
+ .set_fd_flags = tevent_common_fd_set_flags,
+ .add_timer = tevent_wrapper_glue_add_timer,
+ .schedule_immediate = tevent_wrapper_glue_schedule_immediate,
+ .add_signal = tevent_wrapper_glue_add_signal,
+ .loop_once = tevent_wrapper_glue_loop_once,
+ .loop_wait = tevent_wrapper_glue_loop_wait,
+};
+
+static int tevent_wrapper_context_destructor(struct tevent_context *wrap_ev)
+{
+ struct tevent_wrapper_glue *glue = wrap_ev->wrapper.glue;
+ struct tevent_context *main_ev = NULL;
+ struct tevent_fd *fd = NULL, *fn = NULL;
+ struct tevent_timer *te = NULL, *tn = NULL;
+ struct tevent_immediate *ie = NULL, *in = NULL;
+ struct tevent_signal *se = NULL, *sn = NULL;
+#ifdef HAVE_PTHREAD
+ struct tevent_threaded_context *tctx = NULL, *tctxn = NULL;
+#endif
+
+ if (glue == NULL) {
+ tevent_abort(wrap_ev,
+ "tevent_wrapper_context_destructor() active on main");
+ /* static checker support, return below is never reached */
+ return -1;
+ }
+
+ if (glue->destroyed && glue->busy) {
+ tevent_common_check_double_free(wrap_ev,
+ "tevent_context wrapper double free");
+ }
+ glue->destroyed = true;
+
+ if (glue->busy) {
+ return -1;
+ }
+
+ main_ev = glue->main_ev;
+ if (main_ev == NULL) {
+ return 0;
+ }
+
+ TEVENT_DEBUG(wrap_ev, TEVENT_DEBUG_TRACE,
+ "Destroying wrapper context %p \"%s\"\n",
+ wrap_ev, talloc_get_name(glue->private_state));
+
+ glue->main_ev = NULL;
+ DLIST_REMOVE(main_ev->wrapper.list, glue);
+
+#ifdef HAVE_PTHREAD
+ for (tctx = main_ev->threaded_contexts; tctx != NULL; tctx = tctxn) {
+ int ret;
+
+ tctxn = tctx->next;
+
+ if (tctx->event_ctx != glue->wrap_ev) {
+ continue;
+ }
+
+ ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
+ if (ret != 0) {
+ abort();
+ }
+
+ /*
+ * Indicate to the thread that the tevent_context is
+ * gone. The counterpart of this is in
+ * _tevent_threaded_schedule_immediate, there we read
+ * this under the threaded_context's mutex.
+ */
+
+ tctx->event_ctx = NULL;
+
+ ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
+ if (ret != 0) {
+ abort();
+ }
+
+ DLIST_REMOVE(main_ev->threaded_contexts, tctx);
+ }
+#endif
+
+ for (fd = main_ev->fd_events; fd; fd = fn) {
+ fn = fd->next;
+
+ if (fd->wrapper != glue) {
+ continue;
+ }
+
+ tevent_fd_set_flags(fd, 0);
+
+ tevent_common_fd_disarm(fd);
+ }
+
+ for (te = main_ev->timer_events; te; te = tn) {
+ tn = te->next;
+
+ if (te->wrapper != glue) {
+ continue;
+ }
+
+ te->wrapper = NULL;
+ te->event_ctx = NULL;
+
+ if (main_ev->last_zero_timer == te) {
+ main_ev->last_zero_timer = DLIST_PREV(te);
+ }
+ DLIST_REMOVE(main_ev->timer_events, te);
+ }
+
+ for (ie = main_ev->immediate_events; ie; ie = in) {
+ in = ie->next;
+
+ if (ie->wrapper != glue) {
+ continue;
+ }
+
+ ie->wrapper = NULL;
+ ie->event_ctx = NULL;
+ ie->cancel_fn = NULL;
+ DLIST_REMOVE(main_ev->immediate_events, ie);
+ }
+
+ for (se = main_ev->signal_events; se; se = sn) {
+ sn = se->next;
+
+ if (se->wrapper != glue) {
+ continue;
+ }
+
+ se->wrapper = NULL;
+ tevent_cleanup_pending_signal_handlers(se);
+ }
+
+ return 0;
+}
+
+struct tevent_context *_tevent_context_wrapper_create(struct tevent_context *main_ev,
+ TALLOC_CTX *mem_ctx,
+ const struct tevent_wrapper_ops *ops,
+ void *pstate,
+ size_t psize,
+ const char *type,
+ const char *location)
+{
+ void **ppstate = (void **)pstate;
+ struct tevent_context *ev = NULL;
+
+ if (main_ev->wrapper.glue != NULL) {
+ /*
+ * stacking of wrappers is not supported
+ */
+ tevent_debug(main_ev->wrapper.glue->main_ev, TEVENT_DEBUG_FATAL,
+ "%s: %s() stacking not allowed\n",
+ __func__, location);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (main_ev->nesting.allowed) {
+ /*
+ * wrappers conflict with nesting
+ */
+ tevent_debug(main_ev, TEVENT_DEBUG_FATAL,
+ "%s: %s() conflicts with nesting\n",
+ __func__, location);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ev = talloc_zero(mem_ctx, struct tevent_context);
+ if (ev == NULL) {
+ return NULL;
+ }
+ ev->ops = &tevent_wrapper_glue_ops;
+
+ ev->wrapper.glue = talloc_zero(ev, struct tevent_wrapper_glue);
+ if (ev->wrapper.glue == NULL) {
+ talloc_free(ev);
+ return NULL;
+ }
+
+ talloc_set_destructor(ev, tevent_wrapper_context_destructor);
+
+ ev->wrapper.glue->wrap_ev = ev;
+ ev->wrapper.glue->main_ev = main_ev;
+ ev->wrapper.glue->ops = ops;
+ ev->wrapper.glue->private_state = talloc_zero_size(ev->wrapper.glue, psize);
+ if (ev->wrapper.glue->private_state == NULL) {
+ talloc_free(ev);
+ return NULL;
+ }
+ talloc_set_name_const(ev->wrapper.glue->private_state, type);
+
+ DLIST_ADD_END(main_ev->wrapper.list, ev->wrapper.glue);
+
+ *ppstate = ev->wrapper.glue->private_state;
+ return ev;
+}
+
+bool tevent_context_is_wrapper(struct tevent_context *ev)
+{
+ if (ev->wrapper.glue != NULL) {
+ return true;
+ }
+
+ return false;
+}
+
+_PRIVATE_
+struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev)
+{
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ if (ev->wrapper.glue == NULL) {
+ return ev;
+ }
+
+ return ev->wrapper.glue->main_ev;
+}
+
+/*
+ * 32 stack elements should be more than enough
+ *
+ * e.g. Samba uses just 8 elements for [un]become_{root,user}()
+ */
+#define TEVENT_WRAPPER_STACK_SIZE 32
+
+static struct tevent_wrapper_stack {
+ const void *ev_ptr;
+ const struct tevent_wrapper_glue *wrapper;
+} wrapper_stack[TEVENT_WRAPPER_STACK_SIZE];
+
+static size_t wrapper_stack_idx;
+
+_PRIVATE_
+void tevent_wrapper_push_use_internal(struct tevent_context *ev,
+ struct tevent_wrapper_glue *wrapper)
+{
+ /*
+ * ev and wrapper need to belong together!
+ * It's also fine to only have a raw ev
+ * without a wrapper.
+ */
+ if (unlikely(ev->wrapper.glue != wrapper)) {
+ tevent_abort(ev, "tevent_wrapper_push_use_internal() invalid arguments");
+ return;
+ }
+
+ if (wrapper != NULL) {
+ if (unlikely(wrapper->busy)) {
+ tevent_abort(ev, "wrapper already busy!");
+ return;
+ }
+ wrapper->busy = true;
+ }
+
+ if (unlikely(wrapper_stack_idx >= TEVENT_WRAPPER_STACK_SIZE)) {
+ tevent_abort(ev, "TEVENT_WRAPPER_STACK_SIZE overflow");
+ return;
+ }
+
+ wrapper_stack[wrapper_stack_idx] = (struct tevent_wrapper_stack) {
+ .ev_ptr = ev,
+ .wrapper = wrapper,
+ };
+ wrapper_stack_idx++;
+}
+
+_PRIVATE_
+void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr,
+ struct tevent_wrapper_glue *wrapper)
+{
+ struct tevent_context *main_ev = NULL;
+
+ /*
+ * Note that __ev_ptr might a a stale pointer and should not
+ * be touched, we just compare the pointer value in order
+ * to enforce the stack order.
+ */
+
+ if (wrapper != NULL) {
+ main_ev = wrapper->main_ev;
+ }
+
+ if (unlikely(wrapper_stack_idx == 0)) {
+ tevent_abort(main_ev, "tevent_wrapper stack already empty");
+ return;
+ }
+ wrapper_stack_idx--;
+
+ if (wrapper != NULL) {
+ wrapper->busy = false;
+ }
+
+ if (wrapper_stack[wrapper_stack_idx].ev_ptr != __ev_ptr) {
+ tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch ev!");
+ return;
+ }
+ if (wrapper_stack[wrapper_stack_idx].wrapper != wrapper) {
+ tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch wrap!");
+ return;
+ }
+
+ if (wrapper == NULL) {
+ return;
+ }
+
+ if (wrapper->destroyed) {
+ /*
+ * Notice that we can't use TALLOC_FREE()
+ * here because wrapper is a talloc child
+ * of wrapper->wrap_ev.
+ */
+ talloc_free(wrapper->wrap_ev);
+ }
+}
+
+bool _tevent_context_push_use(struct tevent_context *ev,
+ const char *location)
+{
+ bool ok;
+
+ if (ev->wrapper.glue == NULL) {
+ tevent_wrapper_push_use_internal(ev, NULL);
+ return true;
+ }
+
+ if (ev->wrapper.glue->main_ev == NULL) {
+ return false;
+ }
+
+ tevent_wrapper_push_use_internal(ev, ev->wrapper.glue);
+ ok = ev->wrapper.glue->ops->before_use(ev->wrapper.glue->wrap_ev,
+ ev->wrapper.glue->private_state,
+ ev->wrapper.glue->main_ev,
+ location);
+ if (!ok) {
+ tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
+ return false;
+ }
+
+ return true;
+}
+
+void _tevent_context_pop_use(struct tevent_context *ev,
+ const char *location)
+{
+ tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
+
+ if (ev->wrapper.glue == NULL) {
+ return;
+ }
+
+ if (ev->wrapper.glue->main_ev == NULL) {
+ return;
+ }
+
+ ev->wrapper.glue->ops->after_use(ev->wrapper.glue->wrap_ev,
+ ev->wrapper.glue->private_state,
+ ev->wrapper.glue->main_ev,
+ location);
+}
+
+bool tevent_context_same_loop(struct tevent_context *ev1,
+ struct tevent_context *ev2)
+{
+ struct tevent_context *main_ev1 = tevent_wrapper_main_ev(ev1);
+ struct tevent_context *main_ev2 = tevent_wrapper_main_ev(ev2);
+
+ if (main_ev1 == NULL) {
+ return false;
+ }
+
+ if (main_ev1 == main_ev2) {
+ return true;
+ }
+
+ return false;
+}