diff options
Diffstat (limited to 'lib/tevent')
-rw-r--r-- | lib/tevent/ABI/tevent-0.9.36.sigs | 5 | ||||
-rw-r--r-- | lib/tevent/tevent.c | 30 | ||||
-rw-r--r-- | lib/tevent/tevent.h | 260 | ||||
-rw-r--r-- | lib/tevent/tevent_debug.c | 16 | ||||
-rw-r--r-- | lib/tevent/tevent_epoll.c | 8 | ||||
-rw-r--r-- | lib/tevent/tevent_fd.c | 29 | ||||
-rw-r--r-- | lib/tevent/tevent_immediate.c | 27 | ||||
-rw-r--r-- | lib/tevent/tevent_internal.h | 35 | ||||
-rw-r--r-- | lib/tevent/tevent_poll.c | 2 | ||||
-rw-r--r-- | lib/tevent/tevent_signal.c | 32 | ||||
-rw-r--r-- | lib/tevent/tevent_threads.c | 42 | ||||
-rw-r--r-- | lib/tevent/tevent_timed.c | 32 | ||||
-rw-r--r-- | lib/tevent/tevent_wrapper.c | 568 | ||||
-rw-r--r-- | lib/tevent/wscript | 2 |
14 files changed, 1069 insertions, 19 deletions
diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs index 443bb7cb6c9..ddb2c03b65c 100644 --- a/lib/tevent/ABI/tevent-0.9.36.sigs +++ b/lib/tevent/ABI/tevent-0.9.36.sigs @@ -1,6 +1,9 @@ _tevent_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *, int, uint16_t, tevent_fd_handler_t, void *, const char *, const char *) _tevent_add_signal: struct tevent_signal *(struct tevent_context *, TALLOC_CTX *, int, int, tevent_signal_handler_t, void *, const char *, const char *) _tevent_add_timer: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *) +_tevent_context_pop_use: void (struct tevent_context *, const char *) +_tevent_context_push_use: bool (struct tevent_context *, const char *) +_tevent_context_wrapper_create: struct tevent_context *(struct tevent_context *, TALLOC_CTX *, const struct tevent_wrapper_ops *, void *, size_t, const char *, const char *) _tevent_create_immediate: struct tevent_immediate *(TALLOC_CTX *, const char *) _tevent_loop_once: int (struct tevent_context *, const char *) _tevent_loop_until: int (struct tevent_context *, bool (*)(void *), void *, const char *) @@ -47,6 +50,8 @@ tevent_common_wakeup_init: int (struct tevent_context *) tevent_context_init: struct tevent_context *(TALLOC_CTX *) tevent_context_init_byname: struct tevent_context *(TALLOC_CTX *, const char *) tevent_context_init_ops: struct tevent_context *(TALLOC_CTX *, const struct tevent_ops *, void *) +tevent_context_is_wrapper: bool (struct tevent_context *) +tevent_context_same_loop: bool (struct tevent_context *, struct tevent_context *) tevent_debug: void (struct tevent_context *, enum tevent_debug_level, const char *, ...) tevent_fd_get_flags: uint16_t (struct tevent_fd *) tevent_fd_set_auto_close: void (struct tevent_fd *) diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c index de0436df373..dbec1821e41 100644 --- a/lib/tevent/tevent.c +++ b/lib/tevent/tevent.c @@ -298,10 +298,17 @@ int tevent_common_context_destructor(struct tevent_context *ev) struct tevent_timer *te, *tn; struct tevent_immediate *ie, *in; struct tevent_signal *se, *sn; - + struct tevent_wrapper_glue *gl, *gn; #ifdef HAVE_PTHREAD int ret; +#endif + + if (ev->wrapper.glue != NULL) { + tevent_abort(ev, + "tevent_common_context_destructor() active on wrapper"); + } +#ifdef HAVE_PTHREAD ret = pthread_mutex_lock(&tevent_contexts_mutex); if (ret != 0) { abort(); @@ -345,10 +352,18 @@ int tevent_common_context_destructor(struct tevent_context *ev) } #endif + for (gl = ev->wrapper.list; gl; gl = gn) { + gn = gl->next; + + gl->main_ev = NULL; + DLIST_REMOVE(ev->wrapper.list, gl); + } + tevent_common_wakeup_fini(ev); for (fd = ev->fd_events; fd; fd = fn) { fn = fd->next; + fd->wrapper = NULL; fd->event_ctx = NULL; DLIST_REMOVE(ev->fd_events, fd); } @@ -356,12 +371,14 @@ int tevent_common_context_destructor(struct tevent_context *ev) ev->last_zero_timer = NULL; for (te = ev->timer_events; te; te = tn) { tn = te->next; + te->wrapper = NULL; te->event_ctx = NULL; DLIST_REMOVE(ev->timer_events, te); } for (ie = ev->immediate_events; ie; ie = in) { in = ie->next; + ie->wrapper = NULL; ie->event_ctx = NULL; ie->cancel_fn = NULL; DLIST_REMOVE(ev->immediate_events, ie); @@ -369,6 +386,7 @@ int tevent_common_context_destructor(struct tevent_context *ev) for (se = ev->signal_events; se; se = sn) { sn = se->next; + se->wrapper = NULL; se->event_ctx = NULL; DLIST_REMOVE(ev->signal_events, se); /* @@ -675,6 +693,16 @@ struct tevent_signal *_tevent_add_signal(struct tevent_context *ev, void tevent_loop_allow_nesting(struct tevent_context *ev) { + if (ev->wrapper.glue != NULL) { + tevent_abort(ev, "tevent_loop_allow_nesting() on wrapper"); + return; + } + + if (ev->wrapper.list != NULL) { + tevent_abort(ev, "tevent_loop_allow_nesting() with wrapper"); + return; + } + ev->nesting.allowed = true; } diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h index 0e2e806ee5c..d34a03093f3 100644 --- a/lib/tevent/tevent.h +++ b/lib/tevent/tevent.h @@ -429,6 +429,12 @@ int _tevent_loop_wait(struct tevent_context *ev, const char *location); * * @param[in] fde File descriptor event on which to set the destructor * @param[in] close_fn Destructor to execute when fde is freed + * + * @note That the close_fn() on tevent_fd is *NOT* wrapped on contexts + * created by tevent_context_wrapper_create()! + * + * @see tevent_fd_set_close_fn + * @see tevent_context_wrapper_create */ void tevent_fd_set_close_fn(struct tevent_fd *fde, tevent_fd_close_fn_t close_fn); @@ -439,6 +445,8 @@ void tevent_fd_set_close_fn(struct tevent_fd *fde, * This function calls close(fd) internally. * * @param[in] fde File descriptor event to auto-close + * + * @see tevent_fd_set_close_fn */ void tevent_fd_set_auto_close(struct tevent_fd *fde); @@ -1968,6 +1976,258 @@ bool tevent_register_backend(const char *name, const struct tevent_ops *ops); /* @} */ /** + * @defgroup tevent_wrapper_ops The tevent wrapper operation functions + * @ingroup tevent + * + * The following structure and registration functions are exclusively + * needed for people writing wrapper functions for event handlers + * e.g. wrappers can be used for debugging/profiling or impersonation. + * + * There is nothing useful for normal tevent user in here. + * + * @note That the close_fn() on tevent_fd is *NOT* wrapped! + * + * @see tevent_context_wrapper_create + * @see tevent_fd_set_auto_close + * @{ + */ + +struct tevent_wrapper_ops { + const char *name; + + bool (*before_use)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + const char *location); + void (*after_use)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + const char *location); + + void (*before_fd_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_fd *fde, + uint16_t flags, + const char *handler_name, + const char *location); + void (*after_fd_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_fd *fde, + uint16_t flags, + const char *handler_name, + const char *location); + + void (*before_timer_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_timer *te, + struct timeval requested_time, + struct timeval trigger_time, + const char *handler_name, + const char *location); + void (*after_timer_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_timer *te, + struct timeval requested_time, + struct timeval trigger_time, + const char *handler_name, + const char *location); + + void (*before_immediate_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_immediate *im, + const char *handler_name, + const char *location); + void (*after_immediate_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_immediate *im, + const char *handler_name, + const char *location); + + void (*before_signal_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + const char *handler_name, + const char *location); + void (*after_signal_handler)(struct tevent_context *wrap_ev, + void *private_state, + struct tevent_context *main_ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + const char *handler_name, + const char *location); +}; + +#ifdef DOXYGEN +/** + * @brief Create a wrapper tevent_context. + * + * @param[in] main_ev The main event context to work on. + * + * @param[in] mem_ctx The talloc memory context to use. + * + * @param[in] ops The tevent_wrapper_ops function table. + * + * @param[out] private_state The private state use by the wrapper functions. + * + * @param[in] private_type The talloc type of the private_state. + * + * @return The wrapper event context, NULL on error. + * + * @note Available as of tevent 0.9.37 + */ +struct tevent_context *tevent_context_wrapper_create(struct tevent_context *main_ev, + TALLOC_CTX *mem_ctx, + const struct tevent_wrapper_ops *ops, + void **private_state, + const char *private_type); +#else +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); +#define tevent_context_wrapper_create(main_ev, mem_ctx, ops, state, type) \ + _tevent_context_wrapper_create(main_ev, mem_ctx, ops, \ + state, sizeof(type), #type, __location__) +#endif + +/** + * @brief Check if the event context is a wrapper event context. + * + * @param[in] ev The event context to work on. + * + * @return Is a wrapper (true), otherwise (false). + * + * @see tevent_context_wrapper_create() + * + * @note Available as of tevent 0.9.37 + */ +bool tevent_context_is_wrapper(struct tevent_context *ev); + +#ifdef DOXYGEN +/** + * @brief Prepare the environment of a (wrapper) event context. + * + * A caller might call this before passing a wrapper event context + * to a tevent_req based *_send() function. + * + * The wrapper event context might do something like impersonation. + * + * tevent_context_push_use() must always be used in combination + * with tevent_context_pop_use(). + * + * There is a global stack of currently active/busy wrapper event contexts. + * Each wrapper can only appear once on that global stack! + * The stack size is limited to 32 elements, which should be enough + * for all useful scenarios. + * + * In addition to an explicit tevent_context_push_use() also + * the invocation of an immediate, timer or fd handler implicitly + * pushes the wrapper on the stack. + * + * Therefore there are some strict constraints for the usage of + * tevent_context_push_use(): + * - It must not be called from within an event handler + * that already acts on the wrapper. + * - tevent_context_pop_use() must be called before + * leaving the code block that called tevent_context_push_use(). + * - The caller is responsible ensure the correct stack ordering + * - Any violation of these constraints results in calling + * the abort handler of the given tevent context. + * + * Calling tevent_context_push_use() on a raw event context + * still consumes an element on the stack, but it's otherwise + * a no-op. + * + * If tevent_context_push_use() returns false, it means + * that the wrapper's before_use() hook returned this failure, + * in that case you must not call tevent_context_pop_use() as + * the wrapper is not pushed onto the stack. + * + * @param[in] ev The event context to work on. + * + * @return Success (true) or failure (false). + * + * @note This is only needed if wrapper event contexts are in use. + * + * @see tevent_context_pop_use + * + * @note Available as of tevent 0.9.37 + */ +bool tevent_context_push_use(struct tevent_context *ev); +#else +bool _tevent_context_push_use(struct tevent_context *ev, + const char *location); +#define tevent_context_push_use(ev) \ + _tevent_context_push_use(ev, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Release the environment of a (wrapper) event context. + * + * The wrapper event context might undo something like impersonation. + * + * This must be called after a succesful tevent_context_push_use(). + * Any ordering violation results in calling + * the abort handler of the given tevent context. + * + * This basically calls the wrapper's after_use() hook. + * + * @param[in] ev The event context to work on. + * + * @note This is only needed if wrapper event contexts are in use. + * + * @see tevent_context_push_use + * + * @note Available as of tevent 0.9.37 + */ +void tevent_context_pop_use(struct tevent_context *ev); +#else +void _tevent_context_pop_use(struct tevent_context *ev, + const char *location); +#define tevent_context_pop_use(ev) \ + _tevent_context_pop_use(ev, __location__) +#endif + +/** + * @brief Check is the two context pointers belong to the same low level loop + * + * With the introduction of wrapper contexts it's not trivial + * to check if two context pointers belong to the same low level + * event loop. Some code may need to know this in order + * to make some caching decisions. + * + * @param[in] ev1 The first event context. + * @param[in] ev2 The second event context. + * + * @return true if both contexts belong to the same (still existing) context + * loop, false otherwise. + * + * @see tevent_context_wrapper_create + * + * @note Available as of tevent 0.9.37 + */ +bool tevent_context_same_loop(struct tevent_context *ev1, + struct tevent_context *ev2); + +/* @} */ + +/** * @defgroup tevent_compat The tevent compatibility functions * @ingroup tevent * diff --git a/lib/tevent/tevent_debug.c b/lib/tevent/tevent_debug.c index 31da7b96836..0a57639076e 100644 --- a/lib/tevent/tevent_debug.c +++ b/lib/tevent/tevent_debug.c @@ -41,6 +41,13 @@ int tevent_set_debug(struct tevent_context *ev, va_list ap) PRINTF_ATTRIBUTE(3,0), void *context) { + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_debug() on wrapper"); + errno = EINVAL; + return -1; + } + ev->debug_ops.debug = debug; ev->debug_ops.context = context; return 0; @@ -86,6 +93,9 @@ void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level, if (!ev) { return; } + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + } if (ev->debug_ops.debug == NULL) { return; } @@ -98,6 +108,12 @@ void tevent_set_trace_callback(struct tevent_context *ev, tevent_trace_callback_t cb, void *private_data) { + if (ev->wrapper.glue != NULL) { + ev = tevent_wrapper_main_ev(ev); + tevent_abort(ev, "tevent_set_trace_callback() on wrapper"); + return; + } + ev->tracing.callback = cb; ev->tracing.private_data = private_data; } diff --git a/lib/tevent/tevent_epoll.c b/lib/tevent/tevent_epoll.c index 5f7ef5d83d1..9cbe505c98a 100644 --- a/lib/tevent/tevent_epoll.c +++ b/lib/tevent/tevent_epoll.c @@ -323,8 +323,10 @@ static int epoll_add_multiplex_fd(struct epoll_event_context *epoll_ev, "add_fde[%p] mpx_fde[%p] fd[%d] - disabling\n", add_fde, mpx_fde, add_fde->fd); DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde); + mpx_fde->wrapper = NULL; mpx_fde->event_ctx = NULL; DLIST_REMOVE(epoll_ev->ev->fd_events, add_fde); + add_fde->wrapper = NULL; add_fde->event_ctx = NULL; return 0; } else if (ret != 0) { @@ -387,9 +389,11 @@ static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_ "fde[%p] mpx_fde[%p] fd[%d] - disabling\n", fde, mpx_fde, fde->fd); DLIST_REMOVE(epoll_ev->ev->fd_events, fde); + fde->wrapper = NULL; fde->event_ctx = NULL; if (mpx_fde != NULL) { DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde); + mpx_fde->wrapper = NULL; mpx_fde->event_ctx = NULL; } return; @@ -462,9 +466,11 @@ static void epoll_del_event(struct epoll_event_context *epoll_ev, struct tevent_ "fde[%p] mpx_fde[%p] fd[%d] - disabling\n", fde, mpx_fde, fde->fd); DLIST_REMOVE(epoll_ev->ev->fd_events, fde); + fde->wrapper = NULL; fde->event_ctx = NULL; if (mpx_fde != NULL) { DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde); + mpx_fde->wrapper = NULL; mpx_fde->event_ctx = NULL; } return; @@ -511,9 +517,11 @@ static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_ "fde[%p] mpx_fde[%p] fd[%d] - disabling\n", fde, mpx_fde, fde->fd); DLIST_REMOVE(epoll_ev->ev->fd_events, fde); + fde->wrapper = NULL; fde->event_ctx = NULL; if (mpx_fde != NULL) { DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde); + mpx_fde->wrapper = NULL; mpx_fde->event_ctx = NULL; } return; diff --git a/lib/tevent/tevent_fd.c b/lib/tevent/tevent_fd.c index 7859cbb00ef..b92c45f1ddd 100644 --- a/lib/tevent/tevent_fd.c +++ b/lib/tevent/tevent_fd.c @@ -51,6 +51,7 @@ done: if (fde->busy) { return -1; } + fde->wrapper = NULL; return 0; } @@ -109,6 +110,8 @@ void tevent_common_fd_set_close_fn(struct tevent_fd *fde, int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags, bool *removed) { + struct tevent_context *handler_ev = fde->event_ctx; + if (removed != NULL) { *removed = false; } @@ -118,7 +121,31 @@ int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags, } fde->busy = true; - fde->handler(fde->event_ctx, fde, flags, fde->private_data); + if (fde->wrapper != NULL) { + handler_ev = fde->wrapper->wrap_ev; + + tevent_wrapper_push_use_internal(handler_ev, fde->wrapper); + fde->wrapper->ops->before_fd_handler( + fde->wrapper->wrap_ev, + fde->wrapper->private_state, + fde->wrapper->main_ev, + fde, + flags, + fde->handler_name, + fde->location); + } + fde->handler(handler_ev, fde, flags, fde->private_data); + if (fde->wrapper != NULL) { + fde->wrapper->ops->after_fd_handler( + fde->wrapper->wrap_ev, + fde->wrapper->private_state, + fde->wrapper->main_ev, + fde, + flags, + fde->handler_name, + fde->location); + tevent_wrapper_pop_use_internal(handler_ev, fde->wrapper); + } fde->busy = false; if (fde->destroyed) { diff --git a/lib/tevent/tevent_immediate.c b/lib/tevent/tevent_immediate.c index 0649d1eacf2..ef7d8a566c0 100644 --- a/lib/tevent/tevent_immediate.c +++ b/lib/tevent/tevent_immediate.c @@ -100,6 +100,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im, { const char *create_location = im->create_location; bool busy = im->busy; + struct tevent_wrapper_glue *glue = im->wrapper; tevent_common_immediate_cancel(im); @@ -109,6 +110,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im, *im = (struct tevent_immediate) { .event_ctx = ev, + .wrapper = glue, .handler = handler, .private_data = private_data, .handler_name = handler_name, @@ -128,6 +130,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im, int tevent_common_invoke_immediate_handler(struct tevent_immediate *im, bool *removed) { + struct tevent_context *handler_ev = im->event_ctx; struct tevent_context *ev = im->event_ctx; struct tevent_immediate cur = *im; @@ -147,7 +150,29 @@ int tevent_common_invoke_immediate_handler(struct tevent_immediate *im, im->busy = true; im->handler_name = NULL; tevent_common_immediate_cancel(im); - cur.handler(ev, im, cur.private_data); + if (cur.wrapper != NULL) { + handler_ev = cur.wrapper->wrap_ev; + + tevent_wrapper_push_use_internal(handler_ev, cur.wrapper); + cur.wrapper->ops->before_immediate_handler( + cur.wrapper->wrap_ev, + cur.wrapper->private_state, + cur.wrapper->main_ev, + im, + cur.handler_name, + cur.schedule_location); + } + cur.handler(handler_ev, im, cur.private_data); + if (cur.wrapper != NULL) { + cur.wrapper->ops->after_immediate_handler( + cur.wrapper->wrap_ev, + cur.wrapper->private_state, + cur.wrapper->main_ev, + im, + cur.handler_name, + cur.schedule_location); + tevent_wrapper_pop_use_internal(handler_ev, cur.wrapper); + } im->busy = false; if (im->destroyed) { diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h index 1183b9f7f83..17c195816fa 100644 --- a/lib/tevent/tevent_internal.h +++ b/lib/tevent/tevent_internal.h @@ -170,6 +170,7 @@ struct tevent_req { struct tevent_fd { struct tevent_fd *prev, *next; struct tevent_context *event_ctx; + struct tevent_wrapper_glue *wrapper; bool busy; bool destroyed; int fd; @@ -189,6 +190,7 @@ struct tevent_fd { struct tevent_timer { struct tevent_timer *prev, *next; struct tevent_context *event_ctx; + struct tevent_wrapper_glue *wrapper; bool busy; bool destroyed; struct timeval next_event; @@ -205,6 +207,7 @@ struct tevent_timer { struct tevent_immediate { struct tevent_immediate *prev, *next; struct tevent_context *event_ctx; + struct tevent_wrapper_glue *wrapper; bool busy; bool destroyed; tevent_immediate_handler_t handler; @@ -222,6 +225,7 @@ struct tevent_immediate { struct tevent_signal { struct tevent_signal *prev, *next; struct tevent_context *event_ctx; + struct tevent_wrapper_glue *wrapper; bool busy; bool destroyed; int signum; @@ -314,6 +318,18 @@ struct tevent_context { void *private_data; } tracing; + struct { + /* + * This is used on the main event context + */ + struct tevent_wrapper_glue *list; + + /* + * This is used on the wrapper event context + */ + struct tevent_wrapper_glue *glue; + } wrapper; + /* * an optimization pointer into timer_events * used by used by common code via @@ -397,6 +413,25 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se, int signum, int count, void *siginfo, bool *removed); +struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev); + +struct tevent_wrapper_ops; + +struct tevent_wrapper_glue { + struct tevent_wrapper_glue *prev, *next; + struct tevent_context *wrap_ev; + struct tevent_context *main_ev; + bool busy; + bool destroyed; + const struct tevent_wrapper_ops *ops; + void *private_state; +}; + +void tevent_wrapper_push_use_internal(struct tevent_context *ev, + struct tevent_wrapper_glue *wrapper); +void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr, + struct tevent_wrapper_glue *wrapper); + bool tevent_standard_init(void); bool tevent_poll_init(void); bool tevent_poll_event_add_fd_internal(struct tevent_context *ev, diff --git a/lib/tevent/tevent_poll.c b/lib/tevent/tevent_poll.c index 74c418ca421..f4c6c2dbe80 100644 --- a/lib/tevent/tevent_poll.c +++ b/lib/tevent/tevent_poll.c @@ -535,6 +535,7 @@ static int poll_event_loop_poll(struct tevent_context *ev, poll_ev->fdes[idx] = NULL; poll_ev->deleted = true; DLIST_REMOVE(ev->fd_events, fde); + fde->wrapper = NULL; fde->event_ctx = NULL; continue; } @@ -586,6 +587,7 @@ static int poll_event_loop_poll(struct tevent_context *ev, poll_ev->deleted = true; if (fde != NULL) { DLIST_REMOVE(ev->fd_events, fde); + fde->wrapper = NULL; fde->event_ctx = NULL; } } diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c index a787f97b964..5ca0b8d2ab1 100644 --- a/lib/tevent/tevent_signal.c +++ b/lib/tevent/tevent_signal.c @@ -216,6 +216,7 @@ done: if (se->busy) { return -1; } + se->wrapper = NULL; return 0; } @@ -338,6 +339,7 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se, int signum, int count, void *siginfo, bool *removed) { + struct tevent_context *handler_ev = se->event_ctx; bool remove = false; if (removed != NULL) { @@ -349,7 +351,35 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se, } se->busy = true; - se->handler(se->event_ctx, se, signum, count, siginfo, se->private_data); + if (se->wrapper != NULL) { + handler_ev = se->wrapper->wrap_ev; + + tevent_wrapper_push_use_internal(handler_ev, se->wrapper); + se->wrapper->ops->before_signal_handler( + se->wrapper->wrap_ev, + se->wrapper->private_state, + se->wrapper->main_ev, + se, + signum, + count, + siginfo, + se->handler_name, + se->location); + } + se->handler(handler_ev, se, signum, count, siginfo, se->private_data); + if (se->wrapper != NULL) { + se->wrapper->ops->after_signal_handler( + se->wrapper->wrap_ev, + se->wrapper->private_state, + se->wrapper->main_ev, + se, + signum, + count, + siginfo, + se->handler_name, + se->location); + tevent_wrapper_pop_use_internal(handler_ev, se->wrapper); + } se->busy = false; #ifdef SA_RESETHAND diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c index 5d4e0c42676..21a9b686ba9 100644 --- a/lib/tevent/tevent_threads.c +++ b/lib/tevent/tevent_threads.c @@ -217,6 +217,18 @@ struct tevent_thread_proxy *tevent_thread_proxy_create( int pipefds[2]; struct tevent_thread_proxy *tp; + if (dest_ev_ctx->wrapper.glue != NULL) { + /* + * stacking of wrappers is not supported + */ + tevent_debug(dest_ev_ctx->wrapper.glue->main_ev, + TEVENT_DEBUG_FATAL, + "%s() not allowed on a wrapper context\n", + __func__); + errno = EINVAL; + return NULL; + } + tp = talloc_zero(dest_ev_ctx, struct tevent_thread_proxy); if (tp == NULL) { return NULL; @@ -375,10 +387,11 @@ void tevent_thread_proxy_schedule(struct tevent_thread_proxy *tp, static int tevent_threaded_context_destructor( struct tevent_threaded_context *tctx) { + struct tevent_context *main_ev = tevent_wrapper_main_ev(tctx->event_ctx); int ret; - if (tctx->event_ctx != NULL) { - DLIST_REMOVE(tctx->event_ctx->threaded_contexts, tctx); + if (main_ev != NULL) { + DLIST_REMOVE(main_ev->threaded_contexts, tctx); } /* @@ -410,10 +423,11 @@ struct tevent_threaded_context *tevent_threaded_context_create( TALLOC_CTX *mem_ctx, struct tevent_context *ev) { #ifdef HAVE_PTHREAD + struct tevent_context *main_ev = tevent_wrapper_main_ev(ev); struct tevent_threaded_context *tctx; int ret; - ret = tevent_common_wakeup_init(ev); + ret = tevent_common_wakeup_init(main_ev); if (ret != 0) { errno = ret; return NULL; @@ -431,7 +445,7 @@ struct tevent_threaded_context *tevent_threaded_context_create( return NULL; } - DLIST_ADD(ev->threaded_contexts, tctx); + DLIST_ADD(main_ev->threaded_contexts, tctx); talloc_set_destructor(tctx, tevent_threaded_context_destructor); return tctx; @@ -458,7 +472,8 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx, { #ifdef HAVE_PTHREAD const char *create_location = im->create_location; - struct tevent_context *ev; + struct tevent_context *main_ev = NULL; + struct tevent_wrapper_glue *glue = tctx->event_ctx->wrapper.glue; int ret, wakeup_fd; ret = pthread_mutex_lock(&tctx->event_ctx_mutex); @@ -466,9 +481,7 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx, abort(); } - ev = tctx->event_ctx; - - if (ev == NULL) { + if (tctx->event_ctx == NULL) { /* * Our event context is already gone. */ @@ -489,8 +502,11 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx, abort(); } + main_ev = tevent_wrapper_main_ev(tctx->event_ctx); + *im = (struct tevent_immediate) { - .event_ctx = ev, + .event_ctx = tctx->event_ctx, + .wrapper = glue, .handler = handler, .private_data = private_data, .handler_name = handler_name, @@ -506,15 +522,15 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx, */ talloc_set_destructor(im, tevent_threaded_schedule_immediate_destructor); - ret = pthread_mutex_lock(&ev->scheduled_mutex); + ret = pthread_mutex_lock(&main_ev->scheduled_mutex); if (ret != 0) { abort(); } - DLIST_ADD_END(ev->scheduled_immediates, im); - wakeup_fd = ev->wakeup_fd; + DLIST_ADD_END(main_ev->scheduled_immediates, im); + wakeup_fd = main_ev->wakeup_fd; - ret = pthread_mutex_unlock(&ev->scheduled_mutex); + ret = pthread_mutex_unlock(&main_ev->scheduled_mutex); if (ret != 0) { abort(); } diff --git a/lib/tevent/tevent_timed.c b/lib/tevent/tevent_timed.c index cb948d4ba29..b521f096c48 100644 --- a/lib/tevent/tevent_timed.c +++ b/lib/tevent/tevent_timed.c @@ -157,6 +157,7 @@ done: if (te->busy) { return -1; } + te->wrapper = NULL; return 0; } @@ -319,6 +320,8 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te, struct timeval current_time, bool *removed) { + struct tevent_context *handler_ev = te->event_ctx; + if (removed != NULL) { *removed = false; } @@ -349,13 +352,40 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te, * otherwise we pass the current time */ te->busy = true; - te->handler(te->event_ctx, te, current_time, te->private_data); + if (te->wrapper != NULL) { + handler_ev = te->wrapper->wrap_ev; + + tevent_wrapper_push_use_internal(handler_ev, te->wrapper); + te->wrapper->ops->before_timer_handler( + te->wrapper->wrap_ev, + te->wrapper->private_state, + te->wrapper->main_ev, + te, + te->next_event, + current_time, + te->handler_name, + te->location); + } + te->handler(handler_ev, te, current_time, te->private_data); + if (te->wrapper != NULL) { + te->wrapper->ops->after_timer_handler( + te->wrapper->wrap_ev, + te->wrapper->private_state, + te->wrapper->main_ev, + te, + te->next_event, + current_time, + te->handler_name, + te->location); + tevent_wrapper_pop_use_internal(handler_ev, te->wrapper); + } te->busy = false; tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE, "Ending timer event %p \"%s\"\n", te, te->handler_name); + te->wrapper = NULL; te->event_ctx = NULL; talloc_set_destructor(te, NULL); TALLOC_FREE(te); diff --git a/lib/tevent/tevent_wrapper.c b/lib/tevent/tevent_wrapper.c new file mode 100644 index 00000000000..05c4c06968a --- /dev/null +++ b/lib/tevent/tevent_wrapper.c @@ -0,0 +1,568 @@ +/* + 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 +#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"); + } + + 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); + + fd->wrapper = NULL; + fd->event_ctx = NULL; + DLIST_REMOVE(main_ev->fd_events, 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->wrapper.glue->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_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; +} diff --git a/lib/tevent/wscript b/lib/tevent/wscript index 94d190f3b60..2395ead9aa9 100644 --- a/lib/tevent/wscript +++ b/lib/tevent/wscript @@ -77,7 +77,7 @@ def build(bld): bld.RECURSE('lib/talloc') SRC = '''tevent.c tevent_debug.c tevent_fd.c tevent_immediate.c - tevent_queue.c tevent_req.c + tevent_queue.c tevent_req.c tevent_wrapper.c tevent_poll.c tevent_threads.c tevent_signal.c tevent_standard.c tevent_timed.c tevent_util.c tevent_wakeup.c''' |