mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
ac9569b1a6
This allows to specify wrapper tevent_contexts, which adds the ability to run functions before and after the event handler functions. This can be used to implement impersonation hooks or advanced debugging/profiling hooks. We'll undo the 0.9.36 ABI change on the 0.9.37 release at the end of this patchset. Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
569 lines
13 KiB
C
569 lines
13 KiB
C
/*
|
|
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;
|
|
}
|