[PATCH] Add winbind request profiling

Stefan Metzmacher metze at samba.org
Wed Jul 11 20:08:54 UTC 2018


Am 11.07.2018 um 16:45 schrieb Ralph Böhme via samba-technical:
> On Tue, Jul 10, 2018 at 12:13:54AM +0200, Stefan Metzmacher via
> samba-technical wrote:
>> I guess I'll push it in the next days, most likely before 4.9.0rc1.
> 
> as discussed: the two winbindd patches reviewed-by-me.
> 
> *Really* nice stuff! :)

Ok, here's the patchset that passed a few private autobuilds.

Please recheck and push:-)

Thanks!
metze
-------------- next part --------------
From af4747aae19e9a76a23eb0830868c04298b28d6f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 18 Jun 2018 17:59:40 +0200
Subject: [PATCH 01/37] tevent: make use of tevent_common_wakeup() in the poll
 and poll_mt backends

This simplifies the "poll_mt" logic a lot.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_poll.c | 100 ++++-----------------------------------
 1 file changed, 9 insertions(+), 91 deletions(-)

diff --git a/lib/tevent/tevent_poll.c b/lib/tevent/tevent_poll.c
index 09d85fa322ad..23b5779d7048 100644
--- a/lib/tevent/tevent_poll.c
+++ b/lib/tevent/tevent_poll.c
@@ -55,9 +55,9 @@ struct poll_event_context {
 	unsigned num_fds;
 
 	/*
-	 * Signal fd to wake the poll() thread
+	 * use tevent_common_wakeup(ev) to wake the poll() thread
 	 */
-	int signal_fd;
+	bool use_mt_mode;
 };
 
 static int poll_event_context_destructor(struct poll_event_context *poll_ev)
@@ -75,24 +75,6 @@ static int poll_event_context_destructor(struct poll_event_context *poll_ev)
 		fd->event_ctx = NULL;
 		DLIST_REMOVE(poll_ev->disabled, fd);
 	}
-
-	if (poll_ev->signal_fd == -1) {
-		/*
-		 * Non-threaded, no signal pipe
-		 */
-		return 0;
-	}
-
-	close(poll_ev->signal_fd);
-	poll_ev->signal_fd = -1;
-
-	if (poll_ev->num_fds == 0) {
-		return 0;
-	}
-	if (poll_ev->fds[0].fd != -1) {
-		close(poll_ev->fds[0].fd);
-		poll_ev->fds[0].fd = -1;
-	}
 	return 0;
 }
 
@@ -116,30 +98,14 @@ static int poll_event_context_init(struct tevent_context *ev)
 		return -1;
 	}
 	poll_ev->ev = ev;
-	poll_ev->signal_fd = -1;
 	ev->additional_data = poll_ev;
 	talloc_set_destructor(poll_ev, poll_event_context_destructor);
 	return 0;
 }
 
-static bool set_nonblock(int fd)
-{
-	int val;
-
-	val = fcntl(fd, F_GETFL, 0);
-	if (val == -1) {
-		return false;
-	}
-	val |= O_NONBLOCK;
-
-	return (fcntl(fd, F_SETFL, val) != -1);
-}
-
 static int poll_event_context_init_mt(struct tevent_context *ev)
 {
 	struct poll_event_context *poll_ev;
-	struct pollfd *pfd;
-	int fds[2];
 	int ret;
 
 	ret = poll_event_context_init(ev);
@@ -150,67 +116,22 @@ static int poll_event_context_init_mt(struct tevent_context *ev)
 	poll_ev = talloc_get_type_abort(
 		ev->additional_data, struct poll_event_context);
 
-	poll_ev->fds = talloc_zero(poll_ev, struct pollfd);
-	if (poll_ev->fds == NULL) {
-		return -1;
-	}
-
-	ret = pipe(fds);
-	if (ret == -1) {
-		return -1;
-	}
-
-	if (!set_nonblock(fds[0]) || !set_nonblock(fds[1])) {
-		close(fds[0]);
-		close(fds[1]);
-		return -1;
+	ret = tevent_common_wakeup_init(ev);
+	if (ret != 0) {
+		return ret;
 	}
 
-	poll_ev->signal_fd = fds[1];
-
-	pfd = &poll_ev->fds[0];
-	pfd->fd = fds[0];
-	pfd->events = (POLLIN|POLLHUP);
-
-	poll_ev->num_fds = 1;
-
-	talloc_set_destructor(poll_ev, poll_event_context_destructor);
+	poll_ev->use_mt_mode = true;
 
 	return 0;
 }
 
 static void poll_event_wake_pollthread(struct poll_event_context *poll_ev)
 {
-	char c;
-	ssize_t ret;
-
-	if (poll_ev->signal_fd == -1) {
-		return;
-	}
-	c = 0;
-	do {
-		ret = write(poll_ev->signal_fd, &c, sizeof(c));
-	} while ((ret == -1) && (errno == EINTR));
-}
-
-static void poll_event_drain_signal_fd(struct poll_event_context *poll_ev)
-{
-	char buf[16];
-	ssize_t ret;
-	int fd;
-
-	if (poll_ev->signal_fd == -1) {
+	if (!poll_ev->use_mt_mode) {
 		return;
 	}
-
-	if (poll_ev->num_fds < 1) {
-		return;
-	}
-	fd = poll_ev->fds[0].fd;
-
-	do {
-		ret = read(fd, buf, sizeof(buf));
-	} while (ret == sizeof(buf));
+	tevent_common_wakeup(poll_ev->ev);
 }
 
 /*
@@ -392,10 +313,9 @@ static bool poll_event_setup_fresh(struct tevent_context *ev,
 	unsigned num_fresh, num_fds;
 
 	if (poll_ev->deleted) {
-		unsigned first_fd = (poll_ev->signal_fd != -1) ? 1 : 0;
 		unsigned i;
 
-		for (i=first_fd; i < poll_ev->num_fds;) {
+		for (i=0; i < poll_ev->num_fds;) {
 			fde = poll_ev->fdes[i];
 			if (fde != NULL) {
 				i++;
@@ -510,8 +430,6 @@ static int poll_event_loop_poll(struct tevent_context *ev,
 		timeout += (tvalp->tv_usec + 999) / 1000;
 	}
 
-	poll_event_drain_signal_fd(poll_ev);
-
 	if (!poll_event_setup_fresh(ev, poll_ev)) {
 		return -1;
 	}
-- 
2.17.1


From 77fb7311fa9e395fe195d6704f08c035fab13214 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 18 Jun 2018 19:49:52 +0200
Subject: [PATCH 02/37] tevent: rewrite/simplify tevent_poll and maintain
 ev->fd_events correctly

The following patches will rely on having all valid fd events in
ev->fd_events, even if they are temporary disabled with
tevent_set_fd_flags(fde, 0);

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_internal.h |   2 +-
 lib/tevent/tevent_poll.c     | 296 +++++++++++++++++++----------------
 lib/tevent/tevent_standard.c |  16 +-
 3 files changed, 164 insertions(+), 150 deletions(-)

diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index ec3955e70a49..692e8d0e0a2d 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -377,7 +377,7 @@ void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se);
 
 bool tevent_standard_init(void);
 bool tevent_poll_init(void);
-void tevent_poll_event_add_fd_internal(struct tevent_context *ev,
+bool tevent_poll_event_add_fd_internal(struct tevent_context *ev,
 				       struct tevent_fd *fde);
 bool tevent_poll_mt_init(void);
 #ifdef HAVE_EPOLL
diff --git a/lib/tevent/tevent_poll.c b/lib/tevent/tevent_poll.c
index 23b5779d7048..282f3cf3082c 100644
--- a/lib/tevent/tevent_poll.c
+++ b/lib/tevent/tevent_poll.c
@@ -33,15 +33,6 @@ struct poll_event_context {
 	/* a pointer back to the generic event_context */
 	struct tevent_context *ev;
 
-	/*
-	 * A DLIST for fresh fde's added by poll_event_add_fd but not
-	 * picked up yet by poll_event_loop_once
-	 */
-	struct tevent_fd *fresh;
-	/*
-	 * A DLIST for disabled fde's.
-	 */
-	struct tevent_fd *disabled;
 	/*
 	 * one or more events were deleted or disabled
 	 */
@@ -49,10 +40,19 @@ struct poll_event_context {
 
 	/*
 	 * These two arrays are maintained together.
+	 *
+	 * The following is always true:
+	 * num_fds <= num_fdes
+	 *
+	 * new 'fresh' elements are added at the end
+	 * of the 'fdes' array and picked up later
+	 * to the 'fds' array in poll_event_sync_arrays()
+	 * before the poll() syscall.
 	 */
 	struct pollfd *fds;
+	size_t num_fds;
 	struct tevent_fd **fdes;
-	unsigned num_fds;
+	size_t num_fdes;
 
 	/*
 	 * use tevent_common_wakeup(ev) to wake the poll() thread
@@ -60,24 +60,6 @@ struct poll_event_context {
 	bool use_mt_mode;
 };
 
-static int poll_event_context_destructor(struct poll_event_context *poll_ev)
-{
-	struct tevent_fd *fd, *fn;
-
-	for (fd = poll_ev->fresh; fd; fd = fn) {
-		fn = fd->next;
-		fd->event_ctx = NULL;
-		DLIST_REMOVE(poll_ev->fresh, fd);
-	}
-
-	for (fd = poll_ev->disabled; fd; fd = fn) {
-		fn = fd->next;
-		fd->event_ctx = NULL;
-		DLIST_REMOVE(poll_ev->disabled, fd);
-	}
-	return 0;
-}
-
 /*
   create a poll_event_context structure.
 */
@@ -99,7 +81,6 @@ static int poll_event_context_init(struct tevent_context *ev)
 	}
 	poll_ev->ev = ev;
 	ev->additional_data = poll_ev;
-	talloc_set_destructor(poll_ev, poll_event_context_destructor);
 	return 0;
 }
 
@@ -151,10 +132,6 @@ static int poll_event_fd_destructor(struct tevent_fd *fde)
 		ev->additional_data, struct poll_event_context);
 
 	if (del_idx == UINT64_MAX) {
-		struct tevent_fd **listp =
-			(struct tevent_fd **)fde->additional_data;
-
-		DLIST_REMOVE((*listp), fde);
 		goto done;
 	}
 
@@ -184,24 +161,50 @@ static void poll_event_schedule_immediate(struct tevent_immediate *im,
   Private function called by "standard" backend fallback.
   Note this only allows fallback to "poll" backend, not "poll-mt".
 */
-_PRIVATE_ void tevent_poll_event_add_fd_internal(struct tevent_context *ev,
+_PRIVATE_ bool tevent_poll_event_add_fd_internal(struct tevent_context *ev,
 						 struct tevent_fd *fde)
 {
 	struct poll_event_context *poll_ev = talloc_get_type_abort(
 		ev->additional_data, struct poll_event_context);
-	struct tevent_fd **listp;
+	uint64_t fde_idx = UINT64_MAX;
+	size_t num_fdes;
 
-	if (fde->flags != 0) {
-		listp = &poll_ev->fresh;
-	} else {
-		listp = &poll_ev->disabled;
+	fde->additional_flags = UINT64_MAX;
+	talloc_set_destructor(fde, poll_event_fd_destructor);
+
+	if (fde->flags == 0) {
+		/*
+		 * Nothing more to do...
+		 */
+		return true;
 	}
 
-	fde->additional_flags	= UINT64_MAX;
-	fde->additional_data	= listp;
+	/*
+	 * We need to add it to the end of the 'fdes' array.
+	 */
+	num_fdes = poll_ev->num_fdes + 1;
+	if (num_fdes > talloc_array_length(poll_ev->fdes)) {
+		struct tevent_fd **tmp_fdes = NULL;
+		size_t array_length;
 
-	DLIST_ADD((*listp), fde);
-	talloc_set_destructor(fde, poll_event_fd_destructor);
+		array_length = (num_fdes + 15) & ~15; /* round up to 16 */
+
+		tmp_fdes = talloc_realloc(poll_ev,
+					  poll_ev->fdes,
+					  struct tevent_fd *,
+					  array_length);
+		if (tmp_fdes == NULL) {
+			return false;
+		}
+		poll_ev->fdes = tmp_fdes;
+	}
+
+	fde_idx = poll_ev->num_fdes;
+	fde->additional_flags = fde_idx;
+	poll_ev->fdes[fde_idx] = fde;
+	poll_ev->num_fdes++;
+
+	return true;
 }
 
 /*
@@ -219,27 +222,29 @@ static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev,
 	struct poll_event_context *poll_ev = talloc_get_type_abort(
 		ev->additional_data, struct poll_event_context);
 	struct tevent_fd *fde;
+	bool ok;
 
 	if (fd < 0) {
 		return NULL;
 	}
 
-	fde = talloc(mem_ctx ? mem_ctx : ev, struct tevent_fd);
+	fde = tevent_common_add_fd(ev,
+				   mem_ctx,
+				   fd,
+				   flags,
+				   handler,
+				   private_data,
+				   handler_name,
+				   location);
 	if (fde == NULL) {
 		return NULL;
 	}
-	fde->event_ctx		= ev;
-	fde->fd			= fd;
-	fde->flags		= flags;
-	fde->handler		= handler;
-	fde->close_fn		= NULL;
-	fde->private_data	= private_data;
-	fde->handler_name	= handler_name;
-	fde->location		= location;
-	fde->additional_flags	= UINT64_MAX;
-	fde->additional_data	= NULL;
-
-	tevent_poll_event_add_fd_internal(ev, fde);
+
+	ok = tevent_poll_event_add_fd_internal(ev, fde);
+	if (!ok) {
+		TALLOC_FREE(fde);
+		return NULL;
+	}
 	poll_event_wake_pollthread(poll_ev);
 
 	/*
@@ -262,19 +267,20 @@ static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
 	if (ev == NULL) {
 		return;
 	}
+
+	if (fde->flags == flags) {
+		return;
+	}
+
 	poll_ev = talloc_get_type_abort(
 		ev->additional_data, struct poll_event_context);
 
 	fde->flags = flags;
 
 	if (idx == UINT64_MAX) {
-		struct tevent_fd **listp =
-			(struct tevent_fd **)fde->additional_data;
-
 		/*
 		 * We move it between the fresh and disabled lists.
 		 */
-		DLIST_REMOVE((*listp), fde);
 		tevent_poll_event_add_fd_internal(ev, fde);
 		poll_event_wake_pollthread(poll_ev);
 		return;
@@ -287,8 +293,16 @@ static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
 		 */
 		poll_ev->fdes[idx] = NULL;
 		poll_ev->deleted = true;
-		DLIST_REMOVE(ev->fd_events, fde);
-		tevent_poll_event_add_fd_internal(ev, fde);
+		fde->additional_flags = UINT64_MAX;
+		poll_event_wake_pollthread(poll_ev);
+		return;
+	}
+
+	if (idx >= poll_ev->num_fds) {
+		/*
+		 * Not yet added to the
+		 * poll_ev->fds array.
+		 */
 		poll_event_wake_pollthread(poll_ev);
 		return;
 	}
@@ -306,17 +320,18 @@ static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
 	poll_event_wake_pollthread(poll_ev);
 }
 
-static bool poll_event_setup_fresh(struct tevent_context *ev,
+static bool poll_event_sync_arrays(struct tevent_context *ev,
 				   struct poll_event_context *poll_ev)
 {
-	struct tevent_fd *fde, *next;
-	unsigned num_fresh, num_fds;
+	size_t i;
+	size_t array_length;
 
 	if (poll_ev->deleted) {
-		unsigned i;
 
 		for (i=0; i < poll_ev->num_fds;) {
-			fde = poll_ev->fdes[i];
+			struct tevent_fd *fde = poll_ev->fdes[i];
+			size_t ci;
+
 			if (fde != NULL) {
 				i++;
 				continue;
@@ -327,61 +342,63 @@ static bool poll_event_setup_fresh(struct tevent_context *ev,
 			 * from the arrays
 			 */
 			poll_ev->num_fds -= 1;
-			if (poll_ev->num_fds == i) {
-				break;
-			}
-			poll_ev->fds[i] = poll_ev->fds[poll_ev->num_fds];
-			poll_ev->fdes[i] = poll_ev->fdes[poll_ev->num_fds];
-			if (poll_ev->fdes[i] != NULL) {
-				poll_ev->fdes[i]->additional_flags = i;
+			ci = poll_ev->num_fds;
+			if (ci > i) {
+				poll_ev->fds[i] = poll_ev->fds[ci];
+				poll_ev->fdes[i] = poll_ev->fdes[ci];
+				if (poll_ev->fdes[i] != NULL) {
+					poll_ev->fdes[i]->additional_flags = i;
+				}
 			}
+			poll_ev->fds[ci] = (struct pollfd) { .fd = -1 };
+			poll_ev->fdes[ci] = NULL;
 		}
 		poll_ev->deleted = false;
 	}
 
-	if (poll_ev->fresh == NULL) {
+	if (poll_ev->num_fds == poll_ev->num_fdes) {
 		return true;
 	}
 
-	num_fresh = 0;
-	for (fde = poll_ev->fresh; fde; fde = fde->next) {
-		num_fresh += 1;
-	}
-	num_fds = poll_ev->num_fds + num_fresh;
-
 	/*
-	 * We check the length of fdes here. It is the last one
-	 * enlarged, so if the realloc for poll_fd->fdes fails,
-	 * poll_fd->fds will have at least the size of poll_fd->fdes
+	 * Recheck the size of both arrays and make sure
+	 * poll_fd->fds array has at least the size of the
+	 * in use poll_ev->fdes array.
 	 */
+	if (poll_ev->num_fdes > talloc_array_length(poll_ev->fds)) {
+		struct pollfd *tmp_fds = NULL;
 
-	if (num_fds >= talloc_array_length(poll_ev->fdes)) {
-		struct pollfd *tmp_fds;
-		struct tevent_fd **tmp_fdes;
-		unsigned array_length;
-
-		array_length = (num_fds + 15) & ~15; /* round up to 16 */
+		/*
+		 * Make sure both allocated the same length.
+		 */
+		array_length = talloc_array_length(poll_ev->fdes);
 
-		tmp_fds = talloc_realloc(
-			poll_ev, poll_ev->fds, struct pollfd, array_length);
+		tmp_fds = talloc_realloc(poll_ev,
+					 poll_ev->fds,
+					 struct pollfd,
+					 array_length);
 		if (tmp_fds == NULL) {
 			return false;
 		}
 		poll_ev->fds = tmp_fds;
-
-		tmp_fdes = talloc_realloc(
-			poll_ev, poll_ev->fdes, struct tevent_fd *,
-			array_length);
-		if (tmp_fdes == NULL) {
-			return false;
-		}
-		poll_ev->fdes = tmp_fdes;
 	}
 
-	for (fde = poll_ev->fresh; fde; fde = next) {
-		struct pollfd *pfd;
+	/*
+	 * Now setup the new elements.
+	 */
+	for (i = poll_ev->num_fds; i < poll_ev->num_fdes; i++) {
+		struct tevent_fd *fde = poll_ev->fdes[i];
+		struct pollfd *pfd = &poll_ev->fds[poll_ev->num_fds];
+
+		if (fde == NULL) {
+			continue;
+		}
 
-		pfd = &poll_ev->fds[poll_ev->num_fds];
+		if (i > poll_ev->num_fds) {
+			poll_ev->fdes[poll_ev->num_fds] = fde;
+			fde->additional_flags = poll_ev->num_fds;
+			poll_ev->fdes[i] = NULL;
+		}
 
 		pfd->fd = fde->fd;
 		pfd->events = 0;
@@ -394,15 +411,41 @@ static bool poll_event_setup_fresh(struct tevent_context *ev,
 			pfd->events |= (POLLOUT);
 		}
 
-		fde->additional_flags = poll_ev->num_fds;
-		poll_ev->fdes[poll_ev->num_fds] = fde;
+		poll_ev->num_fds += 1;
+	}
+	/* Both are in sync again */
+	poll_ev->num_fdes = poll_ev->num_fds;
 
-		next = fde->next;
-		DLIST_REMOVE(poll_ev->fresh, fde);
-		DLIST_ADD(ev->fd_events, fde);
+	/*
+	 * Check if we should shrink the arrays
+	 * But keep at least 16 elements.
+	 */
 
-		poll_ev->num_fds += 1;
+	array_length = (poll_ev->num_fds + 15) & ~15; /* round up to 16 */
+	array_length = MAX(array_length, 16);
+	if (array_length < talloc_array_length(poll_ev->fdes)) {
+		struct tevent_fd **tmp_fdes = NULL;
+		struct pollfd *tmp_fds = NULL;
+
+		tmp_fdes = talloc_realloc(poll_ev,
+					  poll_ev->fdes,
+					  struct tevent_fd *,
+					  array_length);
+		if (tmp_fdes == NULL) {
+			return false;
+		}
+		poll_ev->fdes = tmp_fdes;
+
+		tmp_fds = talloc_realloc(poll_ev,
+					 poll_ev->fds,
+					 struct pollfd,
+					 array_length);
+		if (tmp_fds == NULL) {
+			return false;
+		}
+		poll_ev->fds = tmp_fds;
 	}
+
 	return true;
 }
 
@@ -420,6 +463,7 @@ static int poll_event_loop_poll(struct tevent_context *ev,
 	struct tevent_fd *fde = NULL;
 	struct tevent_fd *next = NULL;
 	unsigned i;
+	bool ok;
 
 	if (ev->signal_events && tevent_common_check_signal(ev)) {
 		return 0;
@@ -430,7 +474,8 @@ static int poll_event_loop_poll(struct tevent_context *ev,
 		timeout += (tvalp->tv_usec + 999) / 1000;
 	}
 
-	if (!poll_event_setup_fresh(ev, poll_ev)) {
+	ok = poll_event_sync_arrays(ev, poll_ev);
+	if (!ok) {
 		return -1;
 	}
 
@@ -580,33 +625,6 @@ static int poll_event_loop_once(struct tevent_context *ev,
 	return poll_event_loop_poll(ev, &tval);
 }
 
-static int poll_event_loop_wait(struct tevent_context *ev,
-				const char *location)
-{
-	struct poll_event_context *poll_ev = talloc_get_type_abort(
-		ev->additional_data, struct poll_event_context);
-
-	/*
-	 * loop as long as we have events pending
-	 */
-	while (tevent_common_have_events(ev) ||
-	       poll_ev->fresh ||
-	       poll_ev->disabled) {
-		int ret;
-		ret = _tevent_loop_once(ev, location);
-		if (ret != 0) {
-			tevent_debug(ev, TEVENT_DEBUG_FATAL,
-				     "_tevent_loop_once() failed: %d - %s\n",
-				     ret, strerror(errno));
-			return ret;
-		}
-	}
-
-	tevent_debug(ev, TEVENT_DEBUG_WARNING,
-		     "poll_event_loop_wait() out of events\n");
-	return 0;
-}
-
 static const struct tevent_ops poll_event_ops = {
 	.context_init		= poll_event_context_init,
 	.add_fd			= poll_event_add_fd,
@@ -617,7 +635,7 @@ static const struct tevent_ops poll_event_ops = {
 	.schedule_immediate	= tevent_common_schedule_immediate,
 	.add_signal		= tevent_common_add_signal,
 	.loop_once		= poll_event_loop_once,
-	.loop_wait		= poll_event_loop_wait,
+	.loop_wait		= tevent_common_loop_wait,
 };
 
 _PRIVATE_ bool tevent_poll_init(void)
@@ -635,7 +653,7 @@ static const struct tevent_ops poll_event_mt_ops = {
 	.schedule_immediate	= poll_event_schedule_immediate,
 	.add_signal		= tevent_common_add_signal,
 	.loop_once		= poll_event_loop_once,
-	.loop_wait		= poll_event_loop_wait,
+	.loop_wait		= tevent_common_loop_wait,
 };
 
 _PRIVATE_ bool tevent_poll_mt_init(void)
diff --git a/lib/tevent/tevent_standard.c b/lib/tevent/tevent_standard.c
index 30e9b91a8651..38720204e021 100644
--- a/lib/tevent/tevent_standard.c
+++ b/lib/tevent/tevent_standard.c
@@ -63,7 +63,6 @@ static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
 		struct std_event_glue);
 	int ret;
 	struct tevent_fd *fde;
-	struct tevent_fd *fde_next;
 
 	glue->fallback_replay = replay;
 
@@ -86,17 +85,14 @@ static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
 	 * Now we have to change all the existing file descriptor
 	 * events from the epoll backend to the poll backend.
 	 */
-	for (fde = ev->fd_events; fde; fde = fde_next) {
-		/*
-		 * We must remove this fde off the ev->fd_events list.
-		 */
-		fde_next = fde->next;
-
-		/* Remove from the ev->fd_events list. */
-		DLIST_REMOVE(ev->fd_events, fde);
+	for (fde = ev->fd_events; fde; fde = fde->next) {
+		bool ok;
 
 		/* Re-add this event as a poll backend event. */
-		tevent_poll_event_add_fd_internal(ev, fde);
+		ok = tevent_poll_event_add_fd_internal(ev, fde);
+		if (!ok) {
+			return false;
+		}
 	}
 
 	return true;
-- 
2.17.1


From f034cd38ef21af26cc2769df92c228cf3259103a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 23 Sep 2015 04:27:53 +0200
Subject: [PATCH 03/37] tevent.h: improve tevent_req documentation

Document tevent_req naming conventions.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/tevent.h | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h
index 3ccac6a3600c..0e2e806ee5c4 100644
--- a/lib/tevent/tevent.h
+++ b/lib/tevent/tevent.h
@@ -697,6 +697,22 @@ void tevent_get_trace_callback(struct tevent_context *ev,
  * the tevent_req structure can be talloc_free'ed. After it has
  * finished, it should talloc_free'ed by the API user.
  *
+ * tevent_req variable naming conventions:
+ *
+ * The name of the variable pointing to the tevent_req structure
+ * returned by a _send() function SHOULD be named differently between
+ * implementation and caller.
+ *
+ * From the point of view of the implementation (of the _send() and
+ * _recv() functions) the variable returned by tevent_req_create() is
+ * always called @em req.
+ *
+ * While the caller of the _send() function should use @em subreq to
+ * hold the result.
+ *
+ * @see tevent_req_create()
+ * @see tevent_req_fn()
+ *
  * @{
  */
 
@@ -748,9 +764,9 @@ struct tevent_req;
 /**
  * @brief A tevent request callback function.
  *
- * @param[in]  req      The tevent async request which executed this callback.
+ * @param[in]  subreq      The tevent async request which executed this callback.
  */
-typedef void (*tevent_req_fn)(struct tevent_req *req);
+typedef void (*tevent_req_fn)(struct tevent_req *subreq);
 
 /**
  * @brief Set an async request callback.
@@ -799,8 +815,6 @@ void *_tevent_req_callback_data(struct tevent_req *req);
  *
  * @param[in]  req      The structure to get the callback data from.
  *
- * @param[in]  req      The structure to get the data from.
- *
  * @return              The private data or NULL if not set.
  */
 void *tevent_req_callback_data_void(struct tevent_req *req);
-- 
2.17.1


From a879f5ed30106ba4b6852cbd09e928081e4d79c0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 23 Oct 2014 06:54:10 +0200
Subject: [PATCH 04/37] tevent/testsuite: return after torture_fail()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/testsuite.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/tevent/testsuite.c b/lib/tevent/testsuite.c
index 63abbf2dc1a6..08aa7588acee 100644
--- a/lib/tevent/testsuite.c
+++ b/lib/tevent/testsuite.c
@@ -191,8 +191,9 @@ static bool test_event_context(struct torture_context *test,
 	while (!finished) {
 		errno = 0;
 		if (tevent_loop_once(ev_ctx) == -1) {
-			talloc_free(ev_ctx);
+			TALLOC_FREE(ev_ctx);
 			torture_fail(test, talloc_asprintf(test, "Failed event loop %s\n", strerror(errno)));
+			return false;
 		}
 	}
 
-- 
2.17.1


From 139712daacb36af8f9df35c7425f40d7431d968e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 22 Mar 2018 16:51:01 +0100
Subject: [PATCH 05/37] tevent: allow tevent_abort() to cope with ev == NULL

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c
index a2d2003cbf43..3d32fd7e12de 100644
--- a/lib/tevent/tevent.c
+++ b/lib/tevent/tevent.c
@@ -577,8 +577,10 @@ void tevent_set_abort_fn(void (*abort_fn)(const char *reason))
 
 static void tevent_abort(struct tevent_context *ev, const char *reason)
 {
-	tevent_debug(ev, TEVENT_DEBUG_FATAL,
-		     "abort: %s\n", reason);
+	if (ev != NULL) {
+		tevent_debug(ev, TEVENT_DEBUG_FATAL,
+			     "abort: %s\n", reason);
+	}
 
 	if (!tevent_abort_fn) {
 		abort();
-- 
2.17.1


From f3433e1d71572eca51a5bad8f5ad018730f1c8f9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 15:10:00 +0200
Subject: [PATCH 06/37] tevent: make tevent_abort() available for backends

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs | 1 +
 lib/tevent/tevent.c               | 4 +---
 lib/tevent/tevent_internal.h      | 2 ++
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 8a579c8ee7d0..07d6e29fddd4 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -17,6 +17,7 @@ _tevent_req_notify_callback: void (struct tevent_req *, const char *)
 _tevent_req_oom: void (struct tevent_req *, const char *)
 _tevent_schedule_immediate: void (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *)
 _tevent_threaded_schedule_immediate: void (struct tevent_threaded_context *, struct tevent_immediate *, tevent_immediate_handler_t, void *, const char *, const char *)
+tevent_abort: void (struct tevent_context *, const char *)
 tevent_backend_list: const char **(TALLOC_CTX *)
 tevent_cleanup_pending_signal_handlers: void (struct tevent_signal *)
 tevent_common_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *, int, uint16_t, tevent_fd_handler_t, void *, const char *, const char *)
diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c
index 3d32fd7e12de..222a3a1a3ddd 100644
--- a/lib/tevent/tevent.c
+++ b/lib/tevent/tevent.c
@@ -70,8 +70,6 @@
 #include <sys/eventfd.h>
 #endif
 
-static void tevent_abort(struct tevent_context *ev, const char *reason);
-
 struct tevent_ops_list {
 	struct tevent_ops_list *next, *prev;
 	const char *name;
@@ -575,7 +573,7 @@ void tevent_set_abort_fn(void (*abort_fn)(const char *reason))
 	tevent_abort_fn = abort_fn;
 }
 
-static void tevent_abort(struct tevent_context *ev, const char *reason)
+void tevent_abort(struct tevent_context *ev, const char *reason)
 {
 	if (ev != NULL) {
 		tevent_debug(ev, TEVENT_DEBUG_FATAL,
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index 692e8d0e0a2d..b13efedb5d92 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -246,6 +246,8 @@ struct tevent_debug_ops {
 void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level,
 		  const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
 
+void tevent_abort(struct tevent_context *ev, const char *reason);
+
 struct tevent_context {
 	/* the specific events implementation */
 	const struct tevent_ops *ops;
-- 
2.17.1


From ffcaddd4a1b09721ad97dc680969db537e61c661 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 23 Oct 2014 07:15:14 +0200
Subject: [PATCH 07/37] tevent: use struct initializers for tevent_fd

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_fd.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/lib/tevent/tevent_fd.c b/lib/tevent/tevent_fd.c
index 455961b67c84..f33ae841b396 100644
--- a/lib/tevent/tevent_fd.c
+++ b/lib/tevent/tevent_fd.c
@@ -60,16 +60,15 @@ struct tevent_fd *tevent_common_add_fd(struct tevent_context *ev, TALLOC_CTX *me
 	fde = talloc(mem_ctx?mem_ctx:ev, struct tevent_fd);
 	if (!fde) return NULL;
 
-	fde->event_ctx		= ev;
-	fde->fd			= fd;
-	fde->flags		= flags;
-	fde->handler		= handler;
-	fde->close_fn		= NULL;
-	fde->private_data	= private_data;
-	fde->handler_name	= handler_name;
-	fde->location		= location;
-	fde->additional_flags	= 0;
-	fde->additional_data	= NULL;
+	*fde = (struct tevent_fd) {
+		.event_ctx	= ev,
+		.fd		= fd,
+		.flags		= flags,
+		.handler	= handler,
+		.private_data	= private_data,
+		.handler_name	= handler_name,
+		.location	= location,
+	};
 
 	DLIST_ADD(ev->fd_events, fde);
 
-- 
2.17.1


From c06b5bc1e23efa1879544de7d8c88d51771650e2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 23 Oct 2014 07:15:14 +0200
Subject: [PATCH 08/37] tevent: use struct initializers for tevent_timer

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_timed.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/lib/tevent/tevent_timed.c b/lib/tevent/tevent_timed.c
index 92f3ed17b26e..ae1eb98d8b2a 100644
--- a/lib/tevent/tevent_timed.c
+++ b/lib/tevent/tevent_timed.c
@@ -224,13 +224,14 @@ static struct tevent_timer *tevent_common_add_timer_internal(
 	te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
 	if (te == NULL) return NULL;
 
-	te->event_ctx		= ev;
-	te->next_event		= next_event;
-	te->handler		= handler;
-	te->private_data	= private_data;
-	te->handler_name	= handler_name;
-	te->location		= location;
-	te->additional_data	= NULL;
+	*te = (struct tevent_timer) {
+		.event_ctx	= ev,
+		.next_event	= next_event,
+		.handler	= handler,
+		.private_data	= private_data,
+		.handler_name	= handler_name,
+		.location	= location,
+	};
 
 	if (ev->timer_events == NULL) {
 		ev->last_zero_timer = NULL;
-- 
2.17.1


From 518eeab82573b0f4f3b56010dafed8bdcfcbc33f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 23 Oct 2014 07:15:14 +0200
Subject: [PATCH 09/37] tevent: use struct initializers for tevent_signal

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_signal.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index c85e1c528c4e..1283aab48211 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -257,22 +257,23 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 	se = talloc(mem_ctx?mem_ctx:ev, struct tevent_signal);
 	if (se == NULL) return NULL;
 
-	se->event_ctx		= ev;
-	se->signum              = signum;
-	se->sa_flags            = sa_flags;
-	se->handler		= handler;
-	se->private_data	= private_data;
-	se->handler_name	= handler_name;
-	se->location		= location;
-	se->additional_data	= NULL;
-
 	sl = talloc(se, struct tevent_common_signal_list);
 	if (!sl) {
 		talloc_free(se);
 		return NULL;
 	}
 	sl->se = se;
-	se->additional_data	= sl;
+
+	*se = (struct tevent_signal) {
+		.event_ctx	= ev,
+		.signum		= signum,
+		.sa_flags	= sa_flags,
+		.handler	= handler,
+		.private_data	= private_data,
+		.handler_name	= handler_name,
+		.location	= location,
+		.additional_data= sl,
+	};
 
 	/* Ensure, no matter the destruction order, that we always have a handle on the global sig_state */
 	if (!talloc_reference(se, sig_state)) {
-- 
2.17.1


From 9a8dc4d5f2232b4904afac30f75fae2571684942 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 23 Oct 2014 07:15:14 +0200
Subject: [PATCH 10/37] tevent: use struct initializers for tevent_immediate

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_immediate.c | 30 ++++++++++++++++--------------
 lib/tevent/tevent_threads.c   | 16 +++++++++-------
 2 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/lib/tevent/tevent_immediate.c b/lib/tevent/tevent_immediate.c
index 9ff532234306..7b4c3e843ded 100644
--- a/lib/tevent/tevent_immediate.c
+++ b/lib/tevent/tevent_immediate.c
@@ -30,6 +30,8 @@
 
 static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 {
+	const char *create_location = im->create_location;
+
 	if (!im->event_ctx) {
 		return;
 	}
@@ -44,13 +46,10 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 	}
 
 	DLIST_REMOVE(im->event_ctx->immediate_events, im);
-	im->event_ctx		= NULL;
-	im->handler		= NULL;
-	im->private_data	= NULL;
-	im->handler_name	= NULL;
-	im->schedule_location	= NULL;
-	im->cancel_fn		= NULL;
-	im->additional_data	= NULL;
+
+	*im = (struct tevent_immediate) {
+		.create_location	= create_location,
+	};
 
 	talloc_set_destructor(im, NULL);
 }
@@ -74,19 +73,22 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 				      const char *handler_name,
 				      const char *location)
 {
+	const char *create_location = im->create_location;
+
 	tevent_common_immediate_cancel(im);
 
 	if (!handler) {
 		return;
 	}
 
-	im->event_ctx		= ev;
-	im->handler		= handler;
-	im->private_data	= private_data;
-	im->handler_name	= handler_name;
-	im->schedule_location	= location;
-	im->cancel_fn		= NULL;
-	im->additional_data	= NULL;
+	*im = (struct tevent_immediate) {
+		.event_ctx		= ev,
+		.handler		= handler,
+		.private_data		= private_data,
+		.handler_name		= handler_name,
+		.create_location	= create_location,
+		.schedule_location	= location,
+	};
 
 	DLIST_ADD_END(ev->immediate_events, im);
 	talloc_set_destructor(im, tevent_common_immediate_destructor);
diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c
index 2c6e66b09049..728f8416d452 100644
--- a/lib/tevent/tevent_threads.c
+++ b/lib/tevent/tevent_threads.c
@@ -449,6 +449,7 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 					 const char *location)
 {
 #ifdef HAVE_PTHREAD
+	const char *create_location = im->create_location;
 	struct tevent_context *ev;
 	int ret, wakeup_fd;
 
@@ -474,13 +475,14 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 		abort();
 	}
 
-	im->event_ctx		= ev;
-	im->handler		= handler;
-	im->private_data	= private_data;
-	im->handler_name	= handler_name;
-	im->schedule_location	= location;
-	im->cancel_fn		= NULL;
-	im->additional_data	= NULL;
+	*im = (struct tevent_immediate) {
+		.event_ctx		= ev,
+		.handler		= handler,
+		.private_data		= private_data,
+		.handler_name		= handler_name,
+		.create_location	= create_location,
+		.schedule_location	= location,
+	};
 
 	ret = pthread_mutex_lock(&ev->scheduled_mutex);
 	if (ret != 0) {
-- 
2.17.1


From 5dde433b40e35d1f7c14829e73578c28128e1853 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 23 Mar 2018 10:25:27 +0100
Subject: [PATCH 11/37] tevent: use _tevent_schedule_immediate() to move events
 from a thread to the main_ev

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_immediate.c |  8 +++++---
 lib/tevent/tevent_threads.c   | 14 +++++++++++++-
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/lib/tevent/tevent_immediate.c b/lib/tevent/tevent_immediate.c
index 7b4c3e843ded..c640a565b082 100644
--- a/lib/tevent/tevent_immediate.c
+++ b/lib/tevent/tevent_immediate.c
@@ -36,9 +36,11 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 		return;
 	}
 
-	tevent_debug(im->event_ctx, TEVENT_DEBUG_TRACE,
-		     "Cancel immediate event %p \"%s\"\n",
-		     im, im->handler_name);
+	if (im->handler_name != NULL) {
+		tevent_debug(im->event_ctx, TEVENT_DEBUG_TRACE,
+			     "Cancel immediate event %p \"%s\"\n",
+			     im, im->handler_name);
+	}
 
 	/* let the backend free im->additional_data */
 	if (im->cancel_fn) {
diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c
index 728f8416d452..3f91ab8bc2ab 100644
--- a/lib/tevent/tevent_threads.c
+++ b/lib/tevent/tevent_threads.c
@@ -532,8 +532,20 @@ void tevent_common_threaded_activate_immediate(struct tevent_context *ev)
 
 	while (ev->scheduled_immediates != NULL) {
 		struct tevent_immediate *im = ev->scheduled_immediates;
+		struct tevent_immediate copy = *im;
+
 		DLIST_REMOVE(ev->scheduled_immediates, im);
-		DLIST_ADD_END(ev->immediate_events, im);
+
+		tevent_debug(ev, TEVENT_DEBUG_TRACE,
+			     "Schedule immediate event \"%s\": %p from thread into main\n",
+			     im->handler_name, im);
+		im->handler_name = NULL;
+		_tevent_schedule_immediate(im,
+					   ev,
+					   copy.handler,
+					   copy.private_data,
+					   copy.handler_name,
+					   copy.schedule_location);
 	}
 
 	ret = pthread_mutex_unlock(&ev->scheduled_mutex);
-- 
2.17.1


From 2d900369275788bf59b6cdbdcc95468cbf67059d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 23 Mar 2018 10:32:15 +0100
Subject: [PATCH 12/37] tevent: add
 tevent_threaded_schedule_immediate_destructor that just aborts

This will be active while the event is part of the ev->scheduled_immediates
list.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_threads.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c
index 3f91ab8bc2ab..efdac9856dd1 100644
--- a/lib/tevent/tevent_threads.c
+++ b/lib/tevent/tevent_threads.c
@@ -441,6 +441,14 @@ struct tevent_threaded_context *tevent_threaded_context_create(
 #endif
 }
 
+static int tevent_threaded_schedule_immediate_destructor(struct tevent_immediate *im)
+{
+	if (im->event_ctx != NULL) {
+		abort();
+	}
+	return 0;
+}
+
 void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 					 struct tevent_immediate *im,
 					 tevent_immediate_handler_t handler,
@@ -484,6 +492,14 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 		.schedule_location	= location,
 	};
 
+	/*
+	 * Make sure the event won't be destroyed while
+	 * it's part of the ev->scheduled_immediates list.
+	 * _tevent_schedule_immediate() will reset the destructor
+	 * in tevent_common_threaded_activate_immediate().
+	 */
+	talloc_set_destructor(im, tevent_threaded_schedule_immediate_destructor);
+
 	ret = pthread_mutex_lock(&ev->scheduled_mutex);
 	if (ret != 0) {
 		abort();
-- 
2.17.1


From 9f8dd4db09f525190830af7a7c6e041c3be1be5e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 17 Apr 2018 16:33:47 +0200
Subject: [PATCH 13/37] tevent: add tevent_common_check_double_free() helper
 function

This will be used to generically support TALLOC_FREE() on
event which are currently running.

It aborts on every explicit talloc_free(), but ignores implicit
cleanup when the talloc parent is about to go.

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |  1 +
 lib/tevent/tevent.c               | 19 +++++++++++++++++++
 lib/tevent/tevent_internal.h      |  2 ++
 3 files changed, 22 insertions(+)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 07d6e29fddd4..8a9d1a6ba105 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -24,6 +24,7 @@ tevent_common_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *,
 tevent_common_add_signal: struct tevent_signal *(struct tevent_context *, TALLOC_CTX *, int, int, tevent_signal_handler_t, void *, const char *, const char *)
 tevent_common_add_timer: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
 tevent_common_add_timer_v2: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
+tevent_common_check_double_free: void (TALLOC_CTX *, const char *)
 tevent_common_check_signal: int (struct tevent_context *)
 tevent_common_context_destructor: int (struct tevent_context *)
 tevent_common_fd_destructor: int (struct tevent_fd *)
diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c
index 222a3a1a3ddd..de0436df3736 100644
--- a/lib/tevent/tevent.c
+++ b/lib/tevent/tevent.c
@@ -429,6 +429,25 @@ static int tevent_common_context_constructor(struct tevent_context *ev)
 	return 0;
 }
 
+void tevent_common_check_double_free(TALLOC_CTX *ptr, const char *reason)
+{
+	void *parent_ptr = talloc_parent(ptr);
+	size_t parent_blocks = talloc_total_blocks(parent_ptr);
+
+	if (parent_ptr != NULL && parent_blocks == 0) {
+		/*
+		 * This is an implicit talloc free, as we still have a parent
+		 * but it's already being destroyed. Note that
+		 * talloc_total_blocks(ptr) also just returns 0 if a
+		 * talloc_free(ptr) is still in progress of freeing all
+		 * children.
+		 */
+		return;
+	}
+
+	tevent_abort(NULL, reason);
+}
+
 /*
   create a event_context structure for a specific implemementation.
   This must be the first events call, and all subsequent calls pass
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index b13efedb5d92..7e15a59d3fb4 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -248,6 +248,8 @@ void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level,
 
 void tevent_abort(struct tevent_context *ev, const char *reason);
 
+void tevent_common_check_double_free(TALLOC_CTX *ptr, const char *reason);
+
 struct tevent_context {
 	/* the specific events implementation */
 	const struct tevent_ops *ops;
-- 
2.17.1


From 04e4e6ceef00bca9022af4bd602536bcafabb7d7 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 12:02:45 +0200
Subject: [PATCH 14/37] tevent: simplify
 tevent_cleanup_pending_signal_handlers()

Calling tevent_signal_destructor() does the same as se->event_ctx is already
NULL.

This also makes sure we correctly cleanup the SA_SIGINFO array.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_signal.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index 1283aab48211..6bb69d77d7a5 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -194,6 +194,7 @@ static int tevent_signal_destructor(struct tevent_signal *se)
 		DLIST_REMOVE(ev->signal_events, se);
 	}
 
+	se->additional_data = NULL;
 	talloc_free(sl);
 
 	if (sig_state->sig_handlers[se->signum] == NULL) {
@@ -464,18 +465,7 @@ int tevent_common_check_signal(struct tevent_context *ev)
 
 void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se)
 {
-	struct tevent_common_signal_list *sl =
-		talloc_get_type_abort(se->additional_data,
-		struct tevent_common_signal_list);
-
-	tevent_common_signal_list_destructor(sl);
-
-	if (sig_state->sig_handlers[se->signum] == NULL) {
-		if (sig_state->oldact[se->signum]) {
-			sigaction(se->signum, sig_state->oldact[se->signum], NULL);
-			talloc_free(sig_state->oldact[se->signum]);
-			sig_state->oldact[se->signum] = NULL;
-		}
-	}
+	tevent_signal_destructor(se);
+	talloc_set_destructor(se, NULL);
 	return;
 }
-- 
2.17.1


From 508ce07113f908210055c606f2327efc27818833 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 27 Mar 2018 14:30:20 +0200
Subject: [PATCH 15/37] tevent: use talloc_zero() in tevent_signal.c

This might not be strictly required, but it might
avoid problems in future...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_signal.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index 6bb69d77d7a5..73b8de798f69 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -255,10 +255,10 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 		}
 	}
 
-	se = talloc(mem_ctx?mem_ctx:ev, struct tevent_signal);
+	se = talloc_zero(mem_ctx?mem_ctx:ev, struct tevent_signal);
 	if (se == NULL) return NULL;
 
-	sl = talloc(se, struct tevent_common_signal_list);
+	sl = talloc_zero(se, struct tevent_common_signal_list);
 	if (!sl) {
 		talloc_free(se);
 		return NULL;
@@ -303,7 +303,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 			}
 		}
 #endif
-		sig_state->oldact[signum] = talloc(sig_state, struct sigaction);
+		sig_state->oldact[signum] = talloc_zero(sig_state, struct sigaction);
 		if (sig_state->oldact[signum] == NULL) {
 			talloc_free(se);
 			return NULL;
-- 
2.17.1


From 5a3682769847276394e6f41999a222dfe16e8321 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 17 Apr 2018 16:43:54 +0200
Subject: [PATCH 16/37] tevent: simplify tevent_signal_destructor()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent_signal.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index 73b8de798f69..ef2302433168 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -184,31 +184,22 @@ static int tevent_common_signal_list_destructor(struct tevent_common_signal_list
 */
 static int tevent_signal_destructor(struct tevent_signal *se)
 {
-	struct tevent_common_signal_list *sl =
-		talloc_get_type_abort(se->additional_data,
-		struct tevent_common_signal_list);
+	TALLOC_FREE(se->additional_data);
 
-	if (se->event_ctx) {
-		struct tevent_context *ev = se->event_ctx;
-
-		DLIST_REMOVE(ev->signal_events, se);
+	if (se->event_ctx != NULL) {
+		DLIST_REMOVE(se->event_ctx->signal_events, se);
 	}
 
-	se->additional_data = NULL;
-	talloc_free(sl);
-
 	if (sig_state->sig_handlers[se->signum] == NULL) {
 		/* restore old handler, if any */
 		if (sig_state->oldact[se->signum]) {
 			sigaction(se->signum, sig_state->oldact[se->signum], NULL);
-			talloc_free(sig_state->oldact[se->signum]);
-			sig_state->oldact[se->signum] = NULL;
+			TALLOC_FREE(sig_state->oldact[se->signum]);
 		}
 #ifdef SA_SIGINFO
 		if (se->sa_flags & SA_SIGINFO) {
 			if (sig_state->sig_info[se->signum]) {
-				talloc_free(sig_state->sig_info[se->signum]);
-				sig_state->sig_info[se->signum] = NULL;
+				TALLOC_FREE(sig_state->sig_info[se->signum]);
 			}
 		}
 #endif
-- 
2.17.1


From a0b3c4672bd0100ba091de01e607ac5a6481f6b1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 13:01:01 +0200
Subject: [PATCH 17/37] tevent: split out tevent_common_invoke_signal_handler()

As side effect this avoids tricks with tevent_se_exists_destructor() to
figure out if the event handler removed itself.

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |   1 +
 lib/tevent/tevent_internal.h      |   5 ++
 lib/tevent/tevent_signal.c        | 101 +++++++++++++++++++-----------
 3 files changed, 69 insertions(+), 38 deletions(-)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 8a9d1a6ba105..230f6b9f26b1 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -32,6 +32,7 @@ tevent_common_fd_get_flags: uint16_t (struct tevent_fd *)
 tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
 tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
 tevent_common_have_events: bool (struct tevent_context *)
+tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
 tevent_common_loop_immediate: bool (struct tevent_context *)
 tevent_common_loop_timer_delay: struct timeval (struct tevent_context *)
 tevent_common_loop_wait: int (struct tevent_context *, const char *)
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index 7e15a59d3fb4..f36cd219f688 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -216,6 +216,8 @@ struct tevent_immediate {
 struct tevent_signal {
 	struct tevent_signal *prev, *next;
 	struct tevent_context *event_ctx;
+	bool busy;
+	bool destroyed;
 	int signum;
 	int sa_flags;
 	tevent_signal_handler_t handler;
@@ -378,6 +380,9 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 					       const char *location);
 int tevent_common_check_signal(struct tevent_context *ev);
 void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se);
+int tevent_common_invoke_signal_handler(struct tevent_signal *se,
+					int signum, int count, void *siginfo,
+					bool *removed);
 
 bool tevent_standard_init(void);
 bool tevent_poll_init(void);
diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index ef2302433168..a787f97b9649 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -184,6 +184,12 @@ static int tevent_common_signal_list_destructor(struct tevent_common_signal_list
 */
 static int tevent_signal_destructor(struct tevent_signal *se)
 {
+	if (se->destroyed) {
+		tevent_common_check_double_free(se, "tevent_signal double free");
+		goto done;
+	}
+	se->destroyed = true;
+
 	TALLOC_FREE(se->additional_data);
 
 	if (se->event_ctx != NULL) {
@@ -205,6 +211,12 @@ static int tevent_signal_destructor(struct tevent_signal *se)
 #endif
 	}
 
+	se->event_ctx = NULL;
+done:
+	if (se->busy) {
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -322,13 +334,42 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 	return se;
 }
 
-struct tevent_se_exists {
-	struct tevent_se_exists **myself;
-};
-
-static int tevent_se_exists_destructor(struct tevent_se_exists *s)
+int tevent_common_invoke_signal_handler(struct tevent_signal *se,
+					int signum, int count, void *siginfo,
+					bool *removed)
 {
-	*s->myself = NULL;
+	bool remove = false;
+
+	if (removed != NULL) {
+		*removed = false;
+	}
+
+	if (se->event_ctx == NULL) {
+		return 0;
+	}
+
+	se->busy = true;
+	se->handler(se->event_ctx, se, signum, count, siginfo, se->private_data);
+	se->busy = false;
+
+#ifdef SA_RESETHAND
+	if (se->sa_flags & SA_RESETHAND) {
+		remove = true;
+	}
+#endif
+
+	if (se->destroyed) {
+		talloc_set_destructor(se, NULL);
+		remove = true;
+	}
+
+	if (remove) {
+		TALLOC_FREE(se);
+		if (removed != NULL) {
+			*removed = true;
+		}
+	}
+
 	return 0;
 }
 
@@ -348,6 +389,7 @@ int tevent_common_check_signal(struct tevent_context *ev)
 		struct tevent_common_signal_list *sl, *next;
 		struct tevent_sigcounter counter = sig_state->signal_count[i];
 		uint32_t count = tevent_sig_count(counter);
+		int ret;
 #ifdef SA_SIGINFO
 		/* Ensure we null out any stored siginfo_t entries
 		 * after processing for debugging purposes. */
@@ -359,25 +401,9 @@ int tevent_common_check_signal(struct tevent_context *ev)
 		}
 		for (sl=sig_state->sig_handlers[i];sl;sl=next) {
 			struct tevent_signal *se = sl->se;
-			struct tevent_se_exists *exists;
 
 			next = sl->next;
 
-			/*
-			 * We have to be careful to not touch "se"
-			 * after it was deleted in its handler. Thus
-			 * we allocate a child whose destructor will
-			 * tell by nulling out itself that its parent
-			 * is gone.
-			 */
-			exists = talloc(se, struct tevent_se_exists);
-			if (exists == NULL) {
-				continue;
-			}
-			exists->myself = &exists;
-			talloc_set_destructor(
-				exists, tevent_se_exists_destructor);
-
 #ifdef SA_SIGINFO
 			if (se->sa_flags & SA_SIGINFO) {
 				uint32_t j;
@@ -391,29 +417,28 @@ int tevent_common_check_signal(struct tevent_context *ev)
 					 * signals in the ringbuffer. */
 					uint32_t ofs = (counter.seen + j)
 						% TEVENT_SA_INFO_QUEUE_COUNT;
-					se->handler(ev, se, i, 1,
-						    (void*)&sig_state->sig_info[i][ofs],
-						    se->private_data);
-					if (!exists) {
+					bool removed = false;
+
+					ret = tevent_common_invoke_signal_handler(
+						se, i, 1,
+						(void*)&sig_state->sig_info[i][ofs],
+						&removed);
+					if (ret != 0) {
+						tevent_abort(ev, "tevent_common_invoke_signal_handler() failed");
+					}
+					if (removed) {
 						break;
 					}
 				}
-#ifdef SA_RESETHAND
-				if (exists && (se->sa_flags & SA_RESETHAND)) {
-					talloc_free(se);
-				}
-#endif
-				talloc_free(exists);
 				continue;
 			}
 #endif
-			se->handler(ev, se, i, count, NULL, se->private_data);
-#ifdef SA_RESETHAND
-			if (exists && (se->sa_flags & SA_RESETHAND)) {
-				talloc_free(se);
+
+			ret = tevent_common_invoke_signal_handler(se, i, count,
+								  NULL, NULL);
+			if (ret != 0) {
+				tevent_abort(ev, "tevent_common_invoke_signal_handler() failed");
 			}
-#endif
-			talloc_free(exists);
 		}
 
 #ifdef SA_SIGINFO
-- 
2.17.1


From 02332c38abb0589e937ec1d02c928394f5146b4e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 13:08:42 +0200
Subject: [PATCH 18/37] tevent: split out tevent_common_invoke_timer_handler()

As side effect this avoids tricks with an extra
tevent_common_timed_deny_destructor().

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |   1 +
 lib/tevent/tevent_internal.h      |   5 ++
 lib/tevent/tevent_timed.c         | 110 +++++++++++++++++++-----------
 3 files changed, 78 insertions(+), 38 deletions(-)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 230f6b9f26b1..bb89cc72d1e1 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -33,6 +33,7 @@ tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
 tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
 tevent_common_have_events: bool (struct tevent_context *)
 tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
+tevent_common_invoke_timer_handler: int (struct tevent_timer *, struct timeval, bool *)
 tevent_common_loop_immediate: bool (struct tevent_context *)
 tevent_common_loop_timer_delay: struct timeval (struct tevent_context *)
 tevent_common_loop_wait: int (struct tevent_context *, const char *)
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index f36cd219f688..8126414d6836 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -187,6 +187,8 @@ struct tevent_fd {
 struct tevent_timer {
 	struct tevent_timer *prev, *next;
 	struct tevent_context *event_ctx;
+	bool busy;
+	bool destroyed;
 	struct timeval next_event;
 	tevent_timer_handler_t handler;
 	/* this is private for the specific handler */
@@ -355,6 +357,9 @@ struct tevent_timer *tevent_common_add_timer_v2(struct tevent_context *ev,
 					        const char *handler_name,
 					        const char *location);
 struct timeval tevent_common_loop_timer_delay(struct tevent_context *);
+int tevent_common_invoke_timer_handler(struct tevent_timer *te,
+				       struct timeval current_time,
+				       bool *removed);
 
 void tevent_common_schedule_immediate(struct tevent_immediate *im,
 				      struct tevent_context *ev,
diff --git a/lib/tevent/tevent_timed.c b/lib/tevent/tevent_timed.c
index ae1eb98d8b2a..cb948d4ba29e 100644
--- a/lib/tevent/tevent_timed.c
+++ b/lib/tevent/tevent_timed.c
@@ -133,6 +133,12 @@ struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs)
 */
 static int tevent_common_timed_destructor(struct tevent_timer *te)
 {
+	if (te->destroyed) {
+		tevent_common_check_double_free(te, "tevent_timer double free");
+		goto done;
+	}
+	te->destroyed = true;
+
 	if (te->event_ctx == NULL) {
 		return 0;
 	}
@@ -146,12 +152,13 @@ static int tevent_common_timed_destructor(struct tevent_timer *te)
 	}
 	DLIST_REMOVE(te->event_ctx->timer_events, te);
 
-	return 0;
-}
+	te->event_ctx = NULL;
+done:
+	if (te->busy) {
+		return -1;
+	}
 
-static int tevent_common_timed_deny_destructor(struct tevent_timer *te)
-{
-	return -1;
+	return 0;
 }
 
 static void tevent_common_insert_timer(struct tevent_context *ev,
@@ -160,6 +167,11 @@ static void tevent_common_insert_timer(struct tevent_context *ev,
 {
 	struct tevent_timer *prev_te = NULL;
 
+	if (te->destroyed) {
+		tevent_abort(ev, "tevent_timer use after free");
+		return;
+	}
+
 	/* keep the list ordered */
 	if (optimize_zero && tevent_timeval_is_zero(&te->next_event)) {
 		/*
@@ -303,6 +315,57 @@ void tevent_update_timer(struct tevent_timer *te, struct timeval next_event)
 	tevent_common_insert_timer(ev, te, false);
 }
 
+int tevent_common_invoke_timer_handler(struct tevent_timer *te,
+				       struct timeval current_time,
+				       bool *removed)
+{
+	if (removed != NULL) {
+		*removed = false;
+	}
+
+	if (te->event_ctx == NULL) {
+		return 0;
+	}
+
+	/*
+	 * We need to remove the timer from the list before calling the
+	 * handler because in a semi-async inner event loop called from the
+	 * handler we don't want to come across this event again -- vl
+	 */
+	if (te->event_ctx->last_zero_timer == te) {
+		te->event_ctx->last_zero_timer = DLIST_PREV(te);
+	}
+	DLIST_REMOVE(te->event_ctx->timer_events, te);
+
+	tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
+		     "Running timer event %p \"%s\"\n",
+		     te, te->handler_name);
+
+	/*
+	 * If the timed event was registered for a zero current_time,
+	 * then we pass a zero timeval here too! To avoid the
+	 * overhead of gettimeofday() calls.
+	 *
+	 * otherwise we pass the current time
+	 */
+	te->busy = true;
+	te->handler(te->event_ctx, te, current_time, te->private_data);
+	te->busy = false;
+
+	tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
+		     "Ending timer event %p \"%s\"\n",
+		     te, te->handler_name);
+
+	te->event_ctx = NULL;
+	talloc_set_destructor(te, NULL);
+	TALLOC_FREE(te);
+
+	if (removed != NULL) {
+		*removed = true;
+	}
+
+	return 0;
+}
 /*
   do a single event loop using the events defined in ev
 
@@ -313,6 +376,7 @@ struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
 {
 	struct timeval current_time = tevent_timeval_zero();
 	struct tevent_timer *te = ev->timer_events;
+	int ret;
 
 	if (!te) {
 		/* have a default tick time of 30 seconds. This guarantees
@@ -344,40 +408,10 @@ struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
 	/*
 	 * ok, we have a timed event that we'll process ...
 	 */
-
-	/* deny the handler to free the event */
-	talloc_set_destructor(te, tevent_common_timed_deny_destructor);
-
-	/* We need to remove the timer from the list before calling the
-	 * handler because in a semi-async inner event loop called from the
-	 * handler we don't want to come across this event again -- vl */
-	if (ev->last_zero_timer == te) {
-		ev->last_zero_timer = DLIST_PREV(te);
+	ret = tevent_common_invoke_timer_handler(te, current_time, NULL);
+	if (ret != 0) {
+		tevent_abort(ev, "tevent_common_invoke_timer_handler() failed");
 	}
-	DLIST_REMOVE(ev->timer_events, te);
-
-	tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
-		     "Running timer event %p \"%s\"\n",
-		     te, te->handler_name);
-
-	/*
-	 * If the timed event was registered for a zero current_time,
-	 * then we pass a zero timeval here too! To avoid the
-	 * overhead of gettimeofday() calls.
-	 *
-	 * otherwise we pass the current time
-	 */
-	te->handler(ev, te, current_time, te->private_data);
-
-	/* The destructor isn't necessary anymore, we've already removed the
-	 * event from the list. */
-	talloc_set_destructor(te, NULL);
-
-	tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
-		     "Ending timer event %p \"%s\"\n",
-		     te, te->handler_name);
-
-	talloc_free(te);
 
 	return tevent_timeval_zero();
 }
-- 
2.17.1


From b4eb8714b48ea26394d26e630015a137b8089d95 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 13:08:42 +0200
Subject: [PATCH 19/37] tevent: split out
 tevent_common_invoke_immediate_handler()

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |  1 +
 lib/tevent/tevent_immediate.c     | 87 +++++++++++++++++++++++--------
 lib/tevent/tevent_internal.h      |  4 ++
 lib/tevent/tevent_threads.c       |  6 +++
 4 files changed, 75 insertions(+), 23 deletions(-)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index bb89cc72d1e1..451e380688c8 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -32,6 +32,7 @@ tevent_common_fd_get_flags: uint16_t (struct tevent_fd *)
 tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
 tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
 tevent_common_have_events: bool (struct tevent_context *)
+tevent_common_invoke_immediate_handler: int (struct tevent_immediate *, bool *)
 tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
 tevent_common_invoke_timer_handler: int (struct tevent_timer *, struct timeval, bool *)
 tevent_common_loop_immediate: bool (struct tevent_context *)
diff --git a/lib/tevent/tevent_immediate.c b/lib/tevent/tevent_immediate.c
index c640a565b082..0649d1eacf27 100644
--- a/lib/tevent/tevent_immediate.c
+++ b/lib/tevent/tevent_immediate.c
@@ -31,6 +31,12 @@
 static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 {
 	const char *create_location = im->create_location;
+	bool busy = im->busy;
+
+	if (im->destroyed) {
+		tevent_abort(im->event_ctx, "tevent_immediate use after free");
+		return;
+	}
 
 	if (!im->event_ctx) {
 		return;
@@ -51,9 +57,12 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 
 	*im = (struct tevent_immediate) {
 		.create_location	= create_location,
+		.busy			= busy,
 	};
 
-	talloc_set_destructor(im, NULL);
+	if (!busy) {
+		talloc_set_destructor(im, NULL);
+	}
 }
 
 /*
@@ -61,7 +70,21 @@ static void tevent_common_immediate_cancel(struct tevent_immediate *im)
 */
 static int tevent_common_immediate_destructor(struct tevent_immediate *im)
 {
+	if (im->destroyed) {
+		tevent_common_check_double_free(im,
+						"tevent_immediate double free");
+		goto done;
+	}
+
 	tevent_common_immediate_cancel(im);
+
+	im->destroyed = true;
+
+done:
+	if (im->busy) {
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -76,6 +99,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 				      const char *location)
 {
 	const char *create_location = im->create_location;
+	bool busy = im->busy;
 
 	tevent_common_immediate_cancel(im);
 
@@ -90,6 +114,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 		.handler_name		= handler_name,
 		.create_location	= create_location,
 		.schedule_location	= location,
+		.busy			= busy,
 	};
 
 	DLIST_ADD_END(ev->immediate_events, im);
@@ -100,18 +125,14 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 		     handler_name, im);
 }
 
-/*
-  trigger the first immediate event and return true
-  if no event was triggered return false
-*/
-bool tevent_common_loop_immediate(struct tevent_context *ev)
+int tevent_common_invoke_immediate_handler(struct tevent_immediate *im,
+					   bool *removed)
 {
-	struct tevent_immediate *im = ev->immediate_events;
-	tevent_immediate_handler_t handler;
-	void *private_data;
+	struct tevent_context *ev = im->event_ctx;
+	struct tevent_immediate cur = *im;
 
-	if (!im) {
-		return false;
+	if (removed != NULL) {
+		*removed = false;
 	}
 
 	tevent_debug(ev, TEVENT_DEBUG_TRACE,
@@ -122,21 +143,41 @@ bool tevent_common_loop_immediate(struct tevent_context *ev)
 	 * remember the handler and then clear the event
 	 * the handler might reschedule the event
 	 */
-	handler = im->handler;
-	private_data = im->private_data;
 
-	DLIST_REMOVE(im->event_ctx->immediate_events, im);
-	im->event_ctx		= NULL;
-	im->handler		= NULL;
-	im->private_data	= NULL;
-	im->handler_name	= NULL;
-	im->schedule_location	= NULL;
-	im->cancel_fn		= NULL;
-	im->additional_data	= NULL;
+	im->busy = true;
+	im->handler_name = NULL;
+	tevent_common_immediate_cancel(im);
+	cur.handler(ev, im, cur.private_data);
+	im->busy = false;
+
+	if (im->destroyed) {
+		talloc_set_destructor(im, NULL);
+		TALLOC_FREE(im);
+		if (removed != NULL) {
+			*removed = true;
+		}
+	}
+
+	return 0;
+}
+
+/*
+  trigger the first immediate event and return true
+  if no event was triggered return false
+*/
+bool tevent_common_loop_immediate(struct tevent_context *ev)
+{
+	struct tevent_immediate *im = ev->immediate_events;
+	int ret;
 
-	talloc_set_destructor(im, NULL);
+	if (!im) {
+		return false;
+	}
 
-	handler(ev, im, private_data);
+	ret = tevent_common_invoke_immediate_handler(im, NULL);
+	if (ret != 0) {
+		tevent_abort(ev, "tevent_common_invoke_immediate_handler() failed");
+	}
 
 	return true;
 }
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index 8126414d6836..d74684c72c2c 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -203,6 +203,8 @@ struct tevent_timer {
 struct tevent_immediate {
 	struct tevent_immediate *prev, *next;
 	struct tevent_context *event_ctx;
+	bool busy;
+	bool destroyed;
 	tevent_immediate_handler_t handler;
 	/* this is private for the specific handler */
 	void *private_data;
@@ -367,6 +369,8 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 				      void *private_data,
 				      const char *handler_name,
 				      const char *location);
+int tevent_common_invoke_immediate_handler(struct tevent_immediate *im,
+					   bool *removed);
 bool tevent_common_loop_immediate(struct tevent_context *ev);
 void tevent_common_threaded_activate_immediate(struct tevent_context *ev);
 
diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c
index efdac9856dd1..9410765266e4 100644
--- a/lib/tevent/tevent_threads.c
+++ b/lib/tevent/tevent_threads.c
@@ -482,6 +482,12 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 	if ((im->event_ctx != NULL) || (handler == NULL)) {
 		abort();
 	}
+	if (im->destroyed) {
+		abort();
+	}
+	if (im->busy) {
+		abort();
+	}
 
 	*im = (struct tevent_immediate) {
 		.event_ctx		= ev,
-- 
2.17.1


From eb5aa9dfab7e1e3f49f8bbb7cb4969f9913d7901 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 14:45:33 +0200
Subject: [PATCH 20/37] tevent: split out tevent_common_invoke_fd_handler()

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |  1 +
 lib/tevent/tevent_epoll.c         |  3 +--
 lib/tevent/tevent_fd.c            | 39 +++++++++++++++++++++++++++++++
 lib/tevent/tevent_internal.h      |  4 ++++
 lib/tevent/tevent_poll.c          |  3 +--
 lib/tevent/tevent_port.c          |  3 +--
 6 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 451e380688c8..443bb7cb6c92 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -32,6 +32,7 @@ tevent_common_fd_get_flags: uint16_t (struct tevent_fd *)
 tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
 tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
 tevent_common_have_events: bool (struct tevent_context *)
+tevent_common_invoke_fd_handler: int (struct tevent_fd *, uint16_t, bool *)
 tevent_common_invoke_immediate_handler: int (struct tevent_immediate *, bool *)
 tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
 tevent_common_invoke_timer_handler: int (struct tevent_timer *, struct timeval, bool *)
diff --git a/lib/tevent/tevent_epoll.c b/lib/tevent/tevent_epoll.c
index 4147c67af2a0..5f7ef5d83d17 100644
--- a/lib/tevent/tevent_epoll.c
+++ b/lib/tevent/tevent_epoll.c
@@ -725,8 +725,7 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval
 		 */
 		flags &= fde->flags;
 		if (flags) {
-			fde->handler(epoll_ev->ev, fde, flags, fde->private_data);
-			break;
+			return tevent_common_invoke_fd_handler(fde, flags, NULL);
 		}
 	}
 
diff --git a/lib/tevent/tevent_fd.c b/lib/tevent/tevent_fd.c
index f33ae841b396..7859cbb00ef5 100644
--- a/lib/tevent/tevent_fd.c
+++ b/lib/tevent/tevent_fd.c
@@ -30,6 +30,12 @@
 
 int tevent_common_fd_destructor(struct tevent_fd *fde)
 {
+	if (fde->destroyed) {
+		tevent_common_check_double_free(fde, "tevent_fd double free");
+		goto done;
+	}
+	fde->destroyed = true;
+
 	if (fde->event_ctx) {
 		DLIST_REMOVE(fde->event_ctx->fd_events, fde);
 	}
@@ -37,6 +43,13 @@ int tevent_common_fd_destructor(struct tevent_fd *fde)
 	if (fde->close_fn) {
 		fde->close_fn(fde->event_ctx, fde, fde->fd, fde->private_data);
 		fde->fd = -1;
+		fde->close_fn = NULL;
+	}
+
+	fde->event_ctx = NULL;
+done:
+	if (fde->busy) {
+		return -1;
 	}
 
 	return 0;
@@ -92,3 +105,29 @@ void tevent_common_fd_set_close_fn(struct tevent_fd *fde,
 {
 	fde->close_fn = close_fn;
 }
+
+int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags,
+				    bool *removed)
+{
+	if (removed != NULL) {
+		*removed = false;
+	}
+
+	if (fde->event_ctx == NULL) {
+		return 0;
+	}
+
+	fde->busy = true;
+	fde->handler(fde->event_ctx, fde, flags, fde->private_data);
+	fde->busy = false;
+
+	if (fde->destroyed) {
+		talloc_set_destructor(fde, NULL);
+		TALLOC_FREE(fde);
+		if (removed != NULL) {
+			*removed = true;
+		}
+	}
+
+	return 0;
+}
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index d74684c72c2c..1183b9f7f831 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -170,6 +170,8 @@ struct tevent_req {
 struct tevent_fd {
 	struct tevent_fd *prev, *next;
 	struct tevent_context *event_ctx;
+	bool busy;
+	bool destroyed;
 	int fd;
 	uint16_t flags; /* see TEVENT_FD_* flags */
 	tevent_fd_handler_t handler;
@@ -343,6 +345,8 @@ void tevent_common_fd_set_close_fn(struct tevent_fd *fde,
 				   tevent_fd_close_fn_t close_fn);
 uint16_t tevent_common_fd_get_flags(struct tevent_fd *fde);
 void tevent_common_fd_set_flags(struct tevent_fd *fde, uint16_t flags);
+int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags,
+				    bool *removed);
 
 struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev,
 					     TALLOC_CTX *mem_ctx,
diff --git a/lib/tevent/tevent_poll.c b/lib/tevent/tevent_poll.c
index 282f3cf3082c..74c418ca4218 100644
--- a/lib/tevent/tevent_poll.c
+++ b/lib/tevent/tevent_poll.c
@@ -565,8 +565,7 @@ static int poll_event_loop_poll(struct tevent_context *ev,
 		flags &= fde->flags;
 		if (flags != 0) {
 			DLIST_DEMOTE(ev->fd_events, fde);
-			fde->handler(ev, fde, flags, fde->private_data);
-			return 0;
+			return tevent_common_invoke_fd_handler(fde, flags, NULL);
 		}
 	}
 
diff --git a/lib/tevent/tevent_port.c b/lib/tevent/tevent_port.c
index 8cf9fd1a0de7..e91d442389dc 100644
--- a/lib/tevent/tevent_port.c
+++ b/lib/tevent/tevent_port.c
@@ -600,8 +600,7 @@ static int port_event_loop(struct port_event_context *port_ev, struct timeval *t
 		 */
 		flags &= fde->flags;
 		if (flags) {
-			fde->handler(ev, fde, flags, fde->private_data);
-			break;
+			return tevent_common_invoke_fd_handler(fde, flags, NULL);
 		}
 	}
 
-- 
2.17.1


From 2abeb889c45e58fc85dc396e33ce4a0b218f8099 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 May 2018 15:43:12 +0200
Subject: [PATCH 21/37] tevent: make use of #include "system/threads.h"

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/testsuite.c      | 2 +-
 lib/tevent/tevent_threads.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/tevent/testsuite.c b/lib/tevent/testsuite.c
index 08aa7588acee..dba8f5895f76 100644
--- a/lib/tevent/testsuite.c
+++ b/lib/tevent/testsuite.c
@@ -32,7 +32,7 @@
 #include "torture/torture.h"
 #include "torture/local/proto.h"
 #ifdef HAVE_PTHREAD
-#include <pthread.h>
+#include "system/threads.h"
 #include <assert.h>
 #endif
 
diff --git a/lib/tevent/tevent_threads.c b/lib/tevent/tevent_threads.c
index 9410765266e4..5d4e0c426769 100644
--- a/lib/tevent/tevent_threads.c
+++ b/lib/tevent/tevent_threads.c
@@ -28,8 +28,8 @@
 #include "tevent_internal.h"
 #include "tevent_util.h"
 
-#if defined(HAVE_PTHREAD)
-#include <pthread.h>
+#ifdef HAVE_PTHREAD
+#include "system/threads.h"
 
 struct tevent_immediate_list {
 	struct tevent_immediate_list *next, *prev;
-- 
2.17.1


From f1293fc9cff2ff1baeb862f60b13cc7ac2c0c136 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 22 Jul 2014 16:51:38 +0200
Subject: [PATCH 22/37] tevent: add tevent_context_wrapper_create()
 infrastructure

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 at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |   5 +
 lib/tevent/tevent.c               |  30 +-
 lib/tevent/tevent.h               | 260 ++++++++++++++
 lib/tevent/tevent_debug.c         |  16 +
 lib/tevent/tevent_epoll.c         |   8 +
 lib/tevent/tevent_fd.c            |  29 +-
 lib/tevent/tevent_immediate.c     |  27 +-
 lib/tevent/tevent_internal.h      |  35 ++
 lib/tevent/tevent_poll.c          |   2 +
 lib/tevent/tevent_signal.c        |  32 +-
 lib/tevent/tevent_threads.c       |  42 ++-
 lib/tevent/tevent_timed.c         |  32 +-
 lib/tevent/tevent_wrapper.c       | 568 ++++++++++++++++++++++++++++++
 lib/tevent/wscript                |   2 +-
 14 files changed, 1069 insertions(+), 19 deletions(-)
 create mode 100644 lib/tevent/tevent_wrapper.c

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index 443bb7cb6c92..ddb2c03b65c2 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 de0436df3736..dbec1821e41c 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 0e2e806ee5c4..d34a03093f36 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);
 
@@ -1967,6 +1975,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 31da7b968366..0a57639076e2 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 5f7ef5d83d17..9cbe505c98a4 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 7859cbb00ef5..b92c45f1ddde 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 0649d1eacf27..ef7d8a566c0a 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 1183b9f7f831..17c195816fae 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 74c418ca4218..f4c6c2dbe80f 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 a787f97b9649..5ca0b8d2ab11 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 5d4e0c426769..21a9b686ba91 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 cb948d4ba29e..b521f096c48a 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 000000000000..05c4c06968aa
--- /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 94d190f3b605..2395ead9aa93 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'''
 
-- 
2.17.1


From 30d5751927f380f6f4896d92fc4c6da58e8e5566 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 16 Jun 2018 14:12:01 +0200
Subject: [PATCH 23/37] tevent: add a simple wrapper test

This checks that for all supported event types the before and after
handlers are called.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/testsuite.c | 356 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 356 insertions(+)

diff --git a/lib/tevent/testsuite.c b/lib/tevent/testsuite.c
index dba8f5895f76..946dea53532b 100644
--- a/lib/tevent/testsuite.c
+++ b/lib/tevent/testsuite.c
@@ -696,6 +696,358 @@ static bool test_event_fd2(struct torture_context *tctx,
 	return true;
 }
 
+struct test_wrapper_state {
+	struct torture_context *tctx;
+	int num_events;
+	int num_wrap_handlers;
+};
+
+static bool test_wrapper_before_use(struct tevent_context *wrap_ev,
+				    void *private_data,
+				    struct tevent_context *main_ev,
+				    const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+	return true;
+}
+
+static void test_wrapper_after_use(struct tevent_context *wrap_ev,
+				   void *private_data,
+				   struct tevent_context *main_ev,
+				   const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_before_fd_handler(struct tevent_context *wrap_ev,
+					   void *private_data,
+					   struct tevent_context *main_ev,
+					   struct tevent_fd *fde,
+					   uint16_t flags,
+					   const char *handler_name,
+					   const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_after_fd_handler(struct tevent_context *wrap_ev,
+					  void *private_data,
+					  struct tevent_context *main_ev,
+					  struct tevent_fd *fde,
+					  uint16_t flags,
+					  const char *handler_name,
+					  const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_before_timer_handler(struct tevent_context *wrap_ev,
+					      void *private_data,
+					      struct tevent_context *main_ev,
+					      struct tevent_timer *te,
+					      struct timeval requested_time,
+					      struct timeval trigger_time,
+					      const char *handler_name,
+					      const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_after_timer_handler(struct tevent_context *wrap_ev,
+					     void *private_data,
+					     struct tevent_context *main_ev,
+					     struct tevent_timer *te,
+					     struct timeval requested_time,
+					     struct timeval trigger_time,
+					     const char *handler_name,
+					     const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_before_immediate_handler(struct tevent_context *wrap_ev,
+						  void *private_data,
+						  struct tevent_context *main_ev,
+						  struct tevent_immediate *im,
+						  const char *handler_name,
+						  const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_after_immediate_handler(struct tevent_context *wrap_ev,
+						 void *private_data,
+						 struct tevent_context *main_ev,
+						 struct tevent_immediate *im,
+						 const char *handler_name,
+						 const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_before_signal_handler(struct tevent_context *wrap_ev,
+					       void *private_data,
+					       struct tevent_context *main_ev,
+					       struct tevent_signal *se,
+					       int signum,
+					       int count,
+					       void *siginfo,
+					       const char *handler_name,
+					       const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static void test_wrapper_after_signal_handler(struct tevent_context *wrap_ev,
+					      void *private_data,
+					      struct tevent_context *main_ev,
+					      struct tevent_signal *se,
+					      int signum,
+					      int count,
+					      void *siginfo,
+					      const char *handler_name,
+					      const char *location)
+{
+	struct test_wrapper_state *state =
+		talloc_get_type_abort(private_data,
+		struct test_wrapper_state);
+
+	torture_comment(state->tctx, "%s\n", __func__);
+	state->num_wrap_handlers++;
+}
+
+static const struct tevent_wrapper_ops test_wrapper_ops = {
+	.name				= "test_wrapper",
+	.before_use			= test_wrapper_before_use,
+	.after_use			= test_wrapper_after_use,
+	.before_fd_handler		= test_wrapper_before_fd_handler,
+	.after_fd_handler		= test_wrapper_after_fd_handler,
+	.before_timer_handler		= test_wrapper_before_timer_handler,
+	.after_timer_handler		= test_wrapper_after_timer_handler,
+	.before_immediate_handler	= test_wrapper_before_immediate_handler,
+	.after_immediate_handler	= test_wrapper_after_immediate_handler,
+	.before_signal_handler		= test_wrapper_before_signal_handler,
+	.after_signal_handler		= test_wrapper_after_signal_handler,
+};
+
+static void test_wrapper_timer_handler(struct tevent_context *ev,
+				       struct tevent_timer *te,
+				       struct timeval tv,
+				       void *private_data)
+{
+	struct test_wrapper_state *state =
+		(struct test_wrapper_state *)private_data;
+
+
+	torture_comment(state->tctx, "timer handler\n");
+
+	state->num_events++;
+	talloc_free(te);
+	return;
+}
+
+static void test_wrapper_fd_handler(struct tevent_context *ev,
+				    struct tevent_fd *fde,
+				    unsigned short fd_flags,
+				    void *private_data)
+{
+	struct test_wrapper_state *state =
+		(struct test_wrapper_state *)private_data;
+
+	torture_comment(state->tctx, "fd handler\n");
+
+	state->num_events++;
+	talloc_free(fde);
+	return;
+}
+
+static void test_wrapper_immediate_handler(struct tevent_context *ev,
+					   struct tevent_immediate *im,
+					   void *private_data)
+{
+	struct test_wrapper_state *state =
+		(struct test_wrapper_state *)private_data;
+
+	state->num_events++;
+	talloc_free(im);
+
+	torture_comment(state->tctx, "immediate handler\n");
+	return;
+}
+
+static void test_wrapper_signal_handler(struct tevent_context *ev,
+					struct tevent_signal *se,
+					int signum,
+					int count,
+					void *siginfo,
+					void *private_data)
+{
+	struct test_wrapper_state *state =
+		(struct test_wrapper_state *)private_data;
+
+	torture_comment(state->tctx, "signal handler\n");
+
+	state->num_events++;
+	talloc_free(se);
+	return;
+}
+
+static bool test_wrapper(struct torture_context *tctx,
+			 const void *test_data)
+{
+	struct test_wrapper_state *state = NULL;
+	int sock[2] = { -1, -1};
+	uint8_t c = 0;
+	const int num_events = 4;
+	const char *backend = (const char *)test_data;
+	struct tevent_context *ev = NULL;
+	struct tevent_context *wrap_ev = NULL;
+	struct tevent_fd *fde = NULL;
+	struct tevent_timer *te = NULL;
+	struct tevent_signal *se = NULL;
+	struct tevent_immediate *im = NULL;
+	int ret;
+	bool ok = false;
+	bool ret2;
+
+	ev = tevent_context_init_byname(tctx, backend);
+	if (ev == NULL) {
+		torture_skip(tctx, talloc_asprintf(tctx,
+			     "event backend '%s' not supported\n",
+			     backend));
+		return true;
+	}
+
+	tevent_set_debug_stderr(ev);
+	torture_comment(tctx, "tevent backend '%s'\n", backend);
+
+	wrap_ev = tevent_context_wrapper_create(
+		ev, ev,	&test_wrapper_ops, &state, struct test_wrapper_state);
+	torture_assert_not_null_goto(tctx, wrap_ev, ok, done,
+				     "tevent_context_wrapper_create failed\n");
+	*state = (struct test_wrapper_state) {
+		.tctx = tctx,
+	};
+
+	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+	torture_assert_goto(tctx, ret == 0, ok, done, "socketpair failed\n");
+
+	te = tevent_add_timer(wrap_ev, wrap_ev,
+			      timeval_current_ofs(0, 0),
+			      test_wrapper_timer_handler, state);
+	torture_assert_not_null_goto(tctx, te, ok, done,
+				     "tevent_add_timer failed\n");
+
+	fde = tevent_add_fd(wrap_ev, wrap_ev,
+			    sock[1],
+			    TEVENT_FD_READ,
+			    test_wrapper_fd_handler,
+			    state);
+	torture_assert_not_null_goto(tctx, fde, ok, done,
+				     "tevent_add_fd failed\n");
+
+	im = tevent_create_immediate(wrap_ev);
+	torture_assert_not_null_goto(tctx, im, ok, done,
+				     "tevent_create_immediate failed\n");
+
+	se = tevent_add_signal(wrap_ev, wrap_ev,
+			       SIGUSR1,
+			       0,
+			       test_wrapper_signal_handler,
+			       state);
+	torture_assert_not_null_goto(tctx, se, ok, done,
+				     "tevent_add_signal failed\n");
+
+	do_write(sock[0], &c, 1);
+	kill(getpid(), SIGUSR1);
+	tevent_schedule_immediate(im,
+				  wrap_ev,
+				  test_wrapper_immediate_handler,
+				  state);
+
+	ret2 = tevent_context_push_use(wrap_ev);
+	torture_assert_goto(tctx, ret2, ok, done, "tevent_context_push_use(wrap_ev) failed\n");
+	ret2 = tevent_context_push_use(ev);
+	torture_assert_goto(tctx, ret2, ok, pop_use, "tevent_context_push_use(ev) failed\n");
+	tevent_context_pop_use(ev);
+	tevent_context_pop_use(wrap_ev);
+
+	ret = tevent_loop_wait(ev);
+	torture_assert_int_equal_goto(tctx, ret, 0, ok, done, "tevent_loop_wait failed\n");
+
+	torture_comment(tctx, "Num events: %d\n", state->num_events);
+	torture_comment(tctx, "Num wrap handlers: %d\n",
+			state->num_wrap_handlers);
+
+	torture_assert_int_equal_goto(tctx, state->num_events, num_events, ok, done,
+				      "Wrong event count\n");
+	torture_assert_int_equal_goto(tctx, state->num_wrap_handlers,
+				      num_events*2+2,
+				      ok, done, "Wrong wrapper count\n");
+
+	ok = true;
+
+done:
+	TALLOC_FREE(wrap_ev);
+	TALLOC_FREE(ev);
+
+	if (sock[0] != -1) {
+		close(sock[0]);
+	}
+	if (sock[1] != -1) {
+		close(sock[1]);
+	}
+	return ok;
+pop_use:
+	tevent_context_pop_use(wrap_ev);
+	goto done;
+}
+
 #ifdef HAVE_PTHREAD
 
 static pthread_mutex_t threaded_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -1280,6 +1632,10 @@ struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx)
 					       "fd2",
 					       test_event_fd2,
 					       (const void *)list[i]);
+		torture_suite_add_simple_tcase_const(backend_suite,
+					       "wrapper",
+					       test_wrapper,
+					       (const void *)list[i]);
 
 		torture_suite_add_suite(suite, backend_suite);
 	}
-- 
2.17.1


From 4ee9a22c065d6078d9d77047654e6465c723bac0 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 16 Jun 2018 16:55:44 +0200
Subject: [PATCH 24/37] tevent: add a test that frees wrapper_ev with pending
 events

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/testsuite.c | 157 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 157 insertions(+)

diff --git a/lib/tevent/testsuite.c b/lib/tevent/testsuite.c
index 946dea53532b..b0f58efd0938 100644
--- a/lib/tevent/testsuite.c
+++ b/lib/tevent/testsuite.c
@@ -1048,6 +1048,159 @@ pop_use:
 	goto done;
 }
 
+static void test_free_wrapper_signal_handler(struct tevent_context *ev,
+					struct tevent_signal *se,
+					int signum,
+					int count,
+					void *siginfo,
+					void *private_data)
+{
+	struct torture_context *tctx =
+		talloc_get_type_abort(private_data,
+		struct torture_context);
+
+	torture_comment(tctx, "signal handler\n");
+
+	talloc_free(se);
+
+	/*
+	 * signal handlers have highest priority in tevent, so this signal
+	 * handler will always be started before the other handlers
+	 * below. Freeing the (wrapper) event context here tests that the
+	 * wrapper implementation correclty handles the wrapper ev going away
+	 * with pending events.
+	 */
+	talloc_free(ev);
+	return;
+}
+
+static void test_free_wrapper_fd_handler(struct tevent_context *ev,
+					 struct tevent_fd *fde,
+					 unsigned short fd_flags,
+					 void *private_data)
+{
+	/*
+	 * This should never be called as
+	 * test_free_wrapper_signal_handler()
+	 * already destroyed the wrapper tevent_context.
+	 */
+	abort();
+}
+
+static void test_free_wrapper_immediate_handler(struct tevent_context *ev,
+					   struct tevent_immediate *im,
+					   void *private_data)
+{
+	/*
+	 * This should never be called as
+	 * test_free_wrapper_signal_handler()
+	 * already destroyed the wrapper tevent_context.
+	 */
+	abort();
+}
+
+static void test_free_wrapper_timer_handler(struct tevent_context *ev,
+				       struct tevent_timer *te,
+				       struct timeval tv,
+				       void *private_data)
+{
+	/*
+	 * This should never be called as
+	 * test_free_wrapper_signal_handler()
+	 * already destroyed the wrapper tevent_context.
+	 */
+	abort();
+}
+
+static bool test_free_wrapper(struct torture_context *tctx,
+			      const void *test_data)
+{
+	struct test_wrapper_state *state = NULL;
+	int sock[2] = { -1, -1};
+	uint8_t c = 0;
+	const char *backend = (const char *)test_data;
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct tevent_context *ev = NULL;
+	struct tevent_context *wrap_ev = NULL;
+	struct tevent_fd *fde = NULL;
+	struct tevent_timer *te = NULL;
+	struct tevent_signal *se = NULL;
+	struct tevent_immediate *im = NULL;
+	int ret;
+	bool ok = false;
+
+	ev = tevent_context_init_byname(frame, backend);
+	if (ev == NULL) {
+		torture_skip(tctx, talloc_asprintf(tctx,
+			     "event backend '%s' not supported\n",
+			     backend));
+		return true;
+	}
+
+	tevent_set_debug_stderr(ev);
+	torture_comment(tctx, "tevent backend '%s'\n", backend);
+
+	wrap_ev = tevent_context_wrapper_create(
+		ev, ev,	&test_wrapper_ops, &state, struct test_wrapper_state);
+	torture_assert_not_null_goto(tctx, wrap_ev, ok, done,
+				     "tevent_context_wrapper_create failed\n");
+	*state = (struct test_wrapper_state) {
+		.tctx = tctx,
+	};
+
+	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+	torture_assert_goto(tctx, ret == 0, ok, done, "socketpair failed\n");
+
+	fde = tevent_add_fd(wrap_ev, frame,
+			    sock[1],
+			    TEVENT_FD_READ,
+			    test_free_wrapper_fd_handler,
+			    NULL);
+	torture_assert_not_null_goto(tctx, fde, ok, done,
+				     "tevent_add_fd failed\n");
+
+	te = tevent_add_timer(wrap_ev, frame,
+			      timeval_current_ofs(0, 0),
+			      test_free_wrapper_timer_handler, NULL);
+	torture_assert_not_null_goto(tctx, te, ok, done,
+				     "tevent_add_timer failed\n");
+
+	im = tevent_create_immediate(frame);
+	torture_assert_not_null_goto(tctx, im, ok, done,
+				     "tevent_create_immediate failed\n");
+
+	se = tevent_add_signal(wrap_ev, frame,
+			       SIGUSR1,
+			       0,
+			       test_free_wrapper_signal_handler,
+			       tctx);
+	torture_assert_not_null_goto(tctx, se, ok, done,
+				     "tevent_add_signal failed\n");
+
+	do_write(sock[0], &c, 1);
+	kill(getpid(), SIGUSR1);
+	tevent_schedule_immediate(im,
+				  wrap_ev,
+				  test_free_wrapper_immediate_handler,
+				  NULL);
+
+	ret = tevent_loop_wait(ev);
+	torture_assert_goto(tctx, ret == 0, ok, done, "tevent_loop_wait failed\n");
+
+	ok = true;
+
+done:
+	TALLOC_FREE(frame);
+
+	if (sock[0] != -1) {
+		close(sock[0]);
+	}
+	if (sock[1] != -1) {
+		close(sock[1]);
+	}
+	return ok;
+}
+
 #ifdef HAVE_PTHREAD
 
 static pthread_mutex_t threaded_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -1636,6 +1789,10 @@ struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx)
 					       "wrapper",
 					       test_wrapper,
 					       (const void *)list[i]);
+		torture_suite_add_simple_tcase_const(backend_suite,
+					       "free_wrapper",
+					       test_free_wrapper,
+					       (const void *)list[i]);
 
 		torture_suite_add_suite(suite, backend_suite);
 	}
-- 
2.17.1


From 528bce8e557720467c18caff4cb056cccd6a7cb1 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 May 2018 14:01:56 +0200
Subject: [PATCH 25/37] tevent: Add tevent_req_profile

This allows detailed reporting where a tevent_req spends its time

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |  15 +++
 lib/tevent/tevent.h               | 185 +++++++++++++++++++++++++++
 lib/tevent/tevent_internal.h      |  19 +++
 lib/tevent/tevent_req.c           | 203 ++++++++++++++++++++++++++++++
 4 files changed, 422 insertions(+)

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index ddb2c03b65c2..f6227db5c938 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -75,11 +75,25 @@ tevent_re_initialise: int (struct tevent_context *)
 tevent_register_backend: bool (const char *, const struct tevent_ops *)
 tevent_req_default_print: char *(struct tevent_req *, TALLOC_CTX *)
 tevent_req_defer_callback: void (struct tevent_req *, struct tevent_context *)
+tevent_req_get_profile: const struct tevent_req_profile *(struct tevent_req *)
 tevent_req_is_error: bool (struct tevent_req *, enum tevent_req_state *, uint64_t *)
 tevent_req_is_in_progress: bool (struct tevent_req *)
+tevent_req_move_profile: struct tevent_req_profile *(struct tevent_req *, TALLOC_CTX *)
 tevent_req_poll: bool (struct tevent_req *, struct tevent_context *)
 tevent_req_post: struct tevent_req *(struct tevent_req *, struct tevent_context *)
 tevent_req_print: char *(TALLOC_CTX *, struct tevent_req *)
+tevent_req_profile_append_sub: void (struct tevent_req_profile *, struct tevent_req_profile **)
+tevent_req_profile_create: struct tevent_req_profile *(TALLOC_CTX *)
+tevent_req_profile_get_name: void (const struct tevent_req_profile *, const char **)
+tevent_req_profile_get_start: void (const struct tevent_req_profile *, const char **, struct timeval *)
+tevent_req_profile_get_status: void (const struct tevent_req_profile *, pid_t *, enum tevent_req_state *, uint64_t *)
+tevent_req_profile_get_stop: void (const struct tevent_req_profile *, const char **, struct timeval *)
+tevent_req_profile_get_subprofiles: const struct tevent_req_profile *(const struct tevent_req_profile *)
+tevent_req_profile_next: const struct tevent_req_profile *(const struct tevent_req_profile *)
+tevent_req_profile_set_name: bool (struct tevent_req_profile *, const char *)
+tevent_req_profile_set_start: bool (struct tevent_req_profile *, const char *, struct timeval)
+tevent_req_profile_set_status: void (struct tevent_req_profile *, pid_t, enum tevent_req_state, uint64_t)
+tevent_req_profile_set_stop: bool (struct tevent_req_profile *, const char *, struct timeval)
 tevent_req_received: void (struct tevent_req *)
 tevent_req_reset_endtime: void (struct tevent_req *)
 tevent_req_set_callback: void (struct tevent_req *, tevent_req_fn, void *)
@@ -87,6 +101,7 @@ tevent_req_set_cancel_fn: void (struct tevent_req *, tevent_req_cancel_fn)
 tevent_req_set_cleanup_fn: void (struct tevent_req *, tevent_req_cleanup_fn)
 tevent_req_set_endtime: bool (struct tevent_req *, struct tevent_context *, struct timeval)
 tevent_req_set_print_fn: void (struct tevent_req *, tevent_req_print_fn)
+tevent_req_set_profile: bool (struct tevent_req *)
 tevent_sa_info_queue_count: size_t (void)
 tevent_set_abort_fn: void (void (*)(const char *))
 tevent_set_debug: int (struct tevent_context *, void (*)(void *, enum tevent_debug_level, const char *, va_list), void *)
diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h
index d34a03093f36..aa6fe0de2020 100644
--- a/lib/tevent/tevent.h
+++ b/lib/tevent/tevent.h
@@ -1348,6 +1348,191 @@ bool tevent_req_is_error(struct tevent_req *req,
  */
 void tevent_req_received(struct tevent_req *req);
 
+/**
+ * @brief Mark a tevent_req for profiling
+ *
+ * This will turn on profiling for this tevent_req an all subreqs that
+ * are directly started as helper requests off this
+ * tevent_req. subreqs are chained by walking up the talloc_parent
+ * hierarchy at a subreq's tevent_req_create. This means to get the
+ * profiling chain right the subreq that needs to be profiled as part
+ * of this tevent_req's profile must be a talloc child of the requests
+ * state variable.
+ *
+ * @param[in] req The request to do tracing for
+ *
+ * @return        False if the profile could not be activated
+ */
+bool tevent_req_set_profile(struct tevent_req *req);
+
+struct tevent_req_profile;
+
+/**
+ * @brief Get the a request's profile for inspection
+ *
+ * @param[in] req The request to get the profile from
+ *
+ * @return        The request's profile
+ */
+const struct tevent_req_profile *tevent_req_get_profile(
+	struct tevent_req *req);
+
+/**
+ * @brief Move the profile out of a request
+ *
+ * This function detaches the request's profile from the request, so
+ * that the profile can outlive the request in a _recv function.
+ *
+ * @param[in] req     The request to move the profile out of
+ * @param[in] mem_ctx The new talloc context for the profile
+ *
+ * @return            The moved profile
+ */
+
+struct tevent_req_profile *tevent_req_move_profile(struct tevent_req *req,
+						   TALLOC_CTX *mem_ctx);
+
+/**
+ * @brief Get a profile description
+ *
+ * @param[in] profile  The profile to be queried
+ * @param[in] req_name The name of the request (state's name)
+ *
+ * "req_name" after this call is still in talloc-posession of "profile"
+ */
+void tevent_req_profile_get_name(const struct tevent_req_profile *profile,
+				 const char **req_name);
+
+/**
+ * @brief Get a profile's start event data
+ *
+ * @param[in] profile        The profile to be queried
+ * @param[in] start_location The location where this event started
+ * @param[in] start_time     The time this event started
+ *
+ * "start_location" after this call is still in talloc-posession of "profile"
+ */
+void tevent_req_profile_get_start(const struct tevent_req_profile *profile,
+				  const char **start_location,
+				  struct timeval *start_time);
+
+/**
+ * @brief Get a profile's stop event data
+ *
+ * @param[in] profile        The profile to be queried
+ * @param[in] stop_location  The location where this event stopped
+ * @param[in] stop_time      The time this event stopped
+ *
+ * "stop_location" after this call is still in talloc-posession of "profile"
+ */
+void tevent_req_profile_get_stop(const struct tevent_req_profile *profile,
+				 const char **stop_location,
+				 struct timeval *stop_time);
+
+/**
+ * @brief Get a profile's result data
+ *
+ * @param[in] pid        The process where this profile was taken
+ * @param[in] state      The status the profile's tevent_req finished with
+ * @param[in] user_error The user error of the profile's tevent_req
+ */
+void tevent_req_profile_get_status(const struct tevent_req_profile *profile,
+				   pid_t *pid,
+				   enum tevent_req_state *state,
+				   uint64_t *user_error);
+
+/**
+ * @brief Retrieve the first subreq's profile from a profile
+ *
+ * @param[in] profile The profile to query
+ *
+ * @return The first tevent subreq's profile
+ */
+const struct tevent_req_profile *tevent_req_profile_get_subprofiles(
+	const struct tevent_req_profile *profile);
+
+/**
+ * @brief Walk the chain of subreqs
+ *
+ * @param[in] profile The subreq's profile to walk
+ *
+ * @return The next subprofile in the list
+ */
+const struct tevent_req_profile *tevent_req_profile_next(
+	const struct tevent_req_profile *profile);
+
+/**
+ * @brief Create a fresh tevent_req_profile
+ *
+ * @param[in] mem_ctx The talloc context to hang the fresh struct off
+ *
+ * @return The fresh struct
+ */
+struct tevent_req_profile *tevent_req_profile_create(TALLOC_CTX *mem_ctx);
+
+/**
+ * @brief Set a profile's name
+ *
+ * @param[in] profile The profile to set the name for
+ * @param[in] name    The new name for the profile
+ *
+ * @return True if the internal talloc_strdup succeeded
+ */
+bool tevent_req_profile_set_name(struct tevent_req_profile *profile,
+				 const char *name);
+
+/**
+ * @brief Set a profile's start event
+ *
+ * @param[in] profile        The profile to set the start data for
+ * @param[in] start_location The new start location
+ * @param[in] start_time     The new start time
+ *
+ * @return True if the internal talloc_strdup succeeded
+ */
+bool tevent_req_profile_set_start(struct tevent_req_profile *profile,
+				  const char *start_location,
+				  struct timeval start_time);
+
+/**
+ * @brief Set a profile's stop event
+ *
+ * @param[in] profile        The profile to set the stop data for
+ * @param[in] stop_location  The new stop location
+ * @param[in] stop_time      The new stop time
+ *
+ * @return True if the internal talloc_strdup succeeded
+ */
+bool tevent_req_profile_set_stop(struct tevent_req_profile *profile,
+				 const char *stop_location,
+				 struct timeval stop_time);
+
+/**
+ * @brief Set a profile's exit status
+ *
+ * @param[in] profile    The profile to set the exit status for
+ * @param[in] pid        The process where this profile was taken
+ * @param[in] state      The status the profile's tevent_req finished with
+ * @param[in] user_error The user error of the profile's tevent_req
+ */
+void tevent_req_profile_set_status(struct tevent_req_profile *profile,
+				   pid_t pid,
+				   enum tevent_req_state state,
+				   uint64_t user_error);
+
+/**
+ * @brief Add a subprofile to a profile
+ *
+ * @param[in] parent_profile The profile to be modified
+ * @param[in] sub_profile The subreqs profile profile to be added
+ *
+ * "subreq" is talloc_move'ed into "parent_profile", so the talloc
+ * ownership of "sub_profile" changes
+ */
+
+void tevent_req_profile_append_sub(struct tevent_req_profile *parent_profile,
+				   struct tevent_req_profile **sub_profile);
+
 /**
  * @brief Create a tevent subrequest at a given time.
  *
diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h
index 17c195816fae..5365fce35336 100644
--- a/lib/tevent/tevent_internal.h
+++ b/lib/tevent/tevent_internal.h
@@ -164,9 +164,28 @@ struct tevent_req {
 		 *
 		 */
 		struct tevent_timer *timer;
+
+		/**
+		 * @brief The place where profiling data is kept
+		 */
+		struct tevent_req_profile *profile;
 	} internal;
 };
 
+struct tevent_req_profile {
+	struct tevent_req_profile *prev, *next;
+	struct tevent_req_profile *parent;
+	const char *req_name;
+	pid_t pid;
+	const char *start_location;
+	struct timeval start_time;
+	const char *stop_location;
+	struct timeval stop_time;
+	enum tevent_req_state state;
+	uint64_t user_error;
+	struct tevent_req_profile *subprofiles;
+};
+
 struct tevent_fd {
 	struct tevent_fd *prev, *next;
 	struct tevent_context *event_ctx;
diff --git a/lib/tevent/tevent_req.c b/lib/tevent/tevent_req.c
index 15754d361ae0..76e27b8f7e9a 100644
--- a/lib/tevent/tevent_req.c
+++ b/lib/tevent/tevent_req.c
@@ -65,6 +65,7 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
 				    const char *location)
 {
 	struct tevent_req *req;
+	struct tevent_req *parent;
 	void **ppdata = (void **)pdata;
 	void *data;
 	size_t payload;
@@ -103,6 +104,19 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
 
 	talloc_set_destructor(req, tevent_req_destructor);
 
+	parent = talloc_get_type(talloc_parent(mem_ctx), struct tevent_req);
+	if ((parent != NULL) && (parent->internal.profile != NULL)) {
+		bool ok = tevent_req_set_profile(req);
+
+		if (!ok) {
+			TALLOC_FREE(req);
+			return NULL;
+		}
+		req->internal.profile->parent = parent->internal.profile;
+		DLIST_ADD_END(parent->internal.profile->subprofiles,
+			      req->internal.profile);
+	}
+
 	*ppdata = data;
 	return req;
 }
@@ -148,6 +162,7 @@ static void tevent_req_finish(struct tevent_req *req,
 			      enum tevent_req_state state,
 			      const char *location)
 {
+	struct tevent_req_profile *p;
 	/*
 	 * make sure we do not timeout after
 	 * the request was already finished
@@ -159,6 +174,20 @@ static void tevent_req_finish(struct tevent_req *req,
 
 	tevent_req_cleanup(req);
 
+	p = req->internal.profile;
+
+	if (p != NULL) {
+		p->stop_location = location;
+		p->stop_time = tevent_timeval_current();
+		p->state = state;
+		p->user_error = req->internal.error;
+
+		if (p->parent != NULL) {
+			talloc_steal(p->parent, p);
+			req->internal.profile = NULL;
+		}
+	}
+
 	_tevent_req_notify_callback(req, location);
 }
 
@@ -363,3 +392,177 @@ void tevent_req_set_cleanup_fn(struct tevent_req *req, tevent_req_cleanup_fn fn)
 	req->private_cleanup.state = req->internal.state;
 	req->private_cleanup.fn = fn;
 }
+
+static int tevent_req_profile_destructor(struct tevent_req_profile *p);
+
+bool tevent_req_set_profile(struct tevent_req *req)
+{
+	struct tevent_req_profile *p;
+
+	if (req->internal.profile != NULL) {
+		tevent_req_error(req, EINVAL);
+		return false;
+	}
+
+	p = tevent_req_profile_create(req);
+
+	if (tevent_req_nomem(p, req)) {
+		return false;
+	}
+
+	p->req_name = talloc_get_name(req->data);
+	p->start_location = req->internal.create_location;
+	p->start_time = tevent_timeval_current();
+
+	req->internal.profile = p;
+
+	return true;
+}
+
+static int tevent_req_profile_destructor(struct tevent_req_profile *p)
+{
+	if (p->parent != NULL) {
+		DLIST_REMOVE(p->parent->subprofiles, p);
+		p->parent = NULL;
+	}
+
+	while (p->subprofiles != NULL) {
+		p->subprofiles->parent = NULL;
+		DLIST_REMOVE(p->subprofiles, p->subprofiles);
+	}
+
+	return 0;
+}
+
+struct tevent_req_profile *tevent_req_move_profile(struct tevent_req *req,
+						   TALLOC_CTX *mem_ctx)
+{
+	return talloc_move(mem_ctx, &req->internal.profile);
+}
+
+const struct tevent_req_profile *tevent_req_get_profile(
+	struct tevent_req *req)
+{
+	return req->internal.profile;
+}
+
+void tevent_req_profile_get_name(const struct tevent_req_profile *profile,
+				 const char **req_name)
+{
+	if (req_name != NULL) {
+		*req_name = profile->req_name;
+	}
+}
+
+void tevent_req_profile_get_start(const struct tevent_req_profile *profile,
+				  const char **start_location,
+				  struct timeval *start_time)
+{
+	if (start_location != NULL) {
+		*start_location = profile->start_location;
+	}
+	if (start_time != NULL) {
+		*start_time = profile->start_time;
+	}
+}
+
+void tevent_req_profile_get_stop(const struct tevent_req_profile *profile,
+				 const char **stop_location,
+				 struct timeval *stop_time)
+{
+	if (stop_location != NULL) {
+		*stop_location = profile->stop_location;
+	}
+	if (stop_time != NULL) {
+		*stop_time = profile->stop_time;
+	}
+}
+
+void tevent_req_profile_get_status(const struct tevent_req_profile *profile,
+				   pid_t *pid,
+				   enum tevent_req_state *state,
+				   uint64_t *user_error)
+{
+	if (pid != NULL) {
+		*pid = profile->pid;
+	}
+	if (state != NULL) {
+		*state = profile->state;
+	}
+	if (user_error != NULL) {
+		*user_error = profile->user_error;
+	}
+}
+
+const struct tevent_req_profile *tevent_req_profile_get_subprofiles(
+	const struct tevent_req_profile *profile)
+{
+	return profile->subprofiles;
+}
+
+const struct tevent_req_profile *tevent_req_profile_next(
+	const struct tevent_req_profile *profile)
+{
+	return profile->next;
+}
+
+struct tevent_req_profile *tevent_req_profile_create(TALLOC_CTX *mem_ctx)
+{
+	struct tevent_req_profile *result;
+
+	result = talloc_zero(mem_ctx, struct tevent_req_profile);
+	if (result == NULL) {
+		return NULL;
+	}
+	talloc_set_destructor(result, tevent_req_profile_destructor);
+
+	return result;
+}
+
+bool tevent_req_profile_set_name(struct tevent_req_profile *profile,
+				 const char *req_name)
+{
+	profile->req_name = talloc_strdup(profile, req_name);
+	return (profile->req_name != NULL);
+}
+
+bool tevent_req_profile_set_start(struct tevent_req_profile *profile,
+				  const char *start_location,
+				  struct timeval start_time)
+{
+	profile->start_time = start_time;
+
+	profile->start_location = talloc_strdup(profile, start_location);
+	return (profile->start_location != NULL);
+}
+
+bool tevent_req_profile_set_stop(struct tevent_req_profile *profile,
+				 const char *stop_location,
+				 struct timeval stop_time)
+{
+	profile->stop_time = stop_time;
+
+	profile->stop_location = talloc_strdup(profile, stop_location);
+	return (profile->stop_location != NULL);
+}
+
+void tevent_req_profile_set_status(struct tevent_req_profile *profile,
+				   pid_t pid,
+				   enum tevent_req_state state,
+				   uint64_t user_error)
+{
+	profile->pid = pid;
+	profile->state = state;
+	profile->user_error = user_error;
+}
+
+void tevent_req_profile_append_sub(struct tevent_req_profile *parent_profile,
+				   struct tevent_req_profile **sub_profile)
+{
+	struct tevent_req_profile *sub;
+
+	sub = talloc_move(parent_profile, sub_profile);
+
+	sub->parent = parent_profile;
+	DLIST_ADD_END(parent_profile->subprofiles, sub);
+}
-- 
2.17.1


From e4b3510b6ba8098da65bde47a45f1038cdbe1e23 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 14 Aug 2014 21:51:09 +0200
Subject: [PATCH 26/37] tevent: version 0.9.37

* simplify "poll" and "poll_mt" backends
* make tevent_abort() reachable for backends
* add tevent_common_invoke_*_handler() functions
* add tevent_context_same_loop() function
* add tevent_context_wrapper_create() infrastructure
* add tevent_req_profile infrastructure

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/ABI/tevent-0.9.36.sigs |  26 ------
 lib/tevent/ABI/tevent-0.9.37.sigs | 126 ++++++++++++++++++++++++++++++
 lib/tevent/wscript                |   2 +-
 3 files changed, 127 insertions(+), 27 deletions(-)
 create mode 100644 lib/tevent/ABI/tevent-0.9.37.sigs

diff --git a/lib/tevent/ABI/tevent-0.9.36.sigs b/lib/tevent/ABI/tevent-0.9.36.sigs
index f6227db5c938..8a579c8ee7d0 100644
--- a/lib/tevent/ABI/tevent-0.9.36.sigs
+++ b/lib/tevent/ABI/tevent-0.9.36.sigs
@@ -1,9 +1,6 @@
 _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 *)
@@ -20,14 +17,12 @@ _tevent_req_notify_callback: void (struct tevent_req *, const char *)
 _tevent_req_oom: void (struct tevent_req *, const char *)
 _tevent_schedule_immediate: void (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *)
 _tevent_threaded_schedule_immediate: void (struct tevent_threaded_context *, struct tevent_immediate *, tevent_immediate_handler_t, void *, const char *, const char *)
-tevent_abort: void (struct tevent_context *, const char *)
 tevent_backend_list: const char **(TALLOC_CTX *)
 tevent_cleanup_pending_signal_handlers: void (struct tevent_signal *)
 tevent_common_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *, int, uint16_t, tevent_fd_handler_t, void *, const char *, const char *)
 tevent_common_add_signal: struct tevent_signal *(struct tevent_context *, TALLOC_CTX *, int, int, tevent_signal_handler_t, void *, const char *, const char *)
 tevent_common_add_timer: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
 tevent_common_add_timer_v2: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
-tevent_common_check_double_free: void (TALLOC_CTX *, const char *)
 tevent_common_check_signal: int (struct tevent_context *)
 tevent_common_context_destructor: int (struct tevent_context *)
 tevent_common_fd_destructor: int (struct tevent_fd *)
@@ -35,10 +30,6 @@ tevent_common_fd_get_flags: uint16_t (struct tevent_fd *)
 tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
 tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
 tevent_common_have_events: bool (struct tevent_context *)
-tevent_common_invoke_fd_handler: int (struct tevent_fd *, uint16_t, bool *)
-tevent_common_invoke_immediate_handler: int (struct tevent_immediate *, bool *)
-tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
-tevent_common_invoke_timer_handler: int (struct tevent_timer *, struct timeval, bool *)
 tevent_common_loop_immediate: bool (struct tevent_context *)
 tevent_common_loop_timer_delay: struct timeval (struct tevent_context *)
 tevent_common_loop_wait: int (struct tevent_context *, const char *)
@@ -50,8 +41,6 @@ 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 *)
@@ -75,25 +64,11 @@ tevent_re_initialise: int (struct tevent_context *)
 tevent_register_backend: bool (const char *, const struct tevent_ops *)
 tevent_req_default_print: char *(struct tevent_req *, TALLOC_CTX *)
 tevent_req_defer_callback: void (struct tevent_req *, struct tevent_context *)
-tevent_req_get_profile: const struct tevent_req_profile *(struct tevent_req *)
 tevent_req_is_error: bool (struct tevent_req *, enum tevent_req_state *, uint64_t *)
 tevent_req_is_in_progress: bool (struct tevent_req *)
-tevent_req_move_profile: struct tevent_req_profile *(struct tevent_req *, TALLOC_CTX *)
 tevent_req_poll: bool (struct tevent_req *, struct tevent_context *)
 tevent_req_post: struct tevent_req *(struct tevent_req *, struct tevent_context *)
 tevent_req_print: char *(TALLOC_CTX *, struct tevent_req *)
-tevent_req_profile_append_sub: void (struct tevent_req_profile *, struct tevent_req_profile **)
-tevent_req_profile_create: struct tevent_req_profile *(TALLOC_CTX *)
-tevent_req_profile_get_name: void (const struct tevent_req_profile *, const char **)
-tevent_req_profile_get_start: void (const struct tevent_req_profile *, const char **, struct timeval *)
-tevent_req_profile_get_status: void (const struct tevent_req_profile *, pid_t *, enum tevent_req_state *, uint64_t *)
-tevent_req_profile_get_stop: void (const struct tevent_req_profile *, const char **, struct timeval *)
-tevent_req_profile_get_subprofiles: const struct tevent_req_profile *(const struct tevent_req_profile *)
-tevent_req_profile_next: const struct tevent_req_profile *(const struct tevent_req_profile *)
-tevent_req_profile_set_name: bool (struct tevent_req_profile *, const char *)
-tevent_req_profile_set_start: bool (struct tevent_req_profile *, const char *, struct timeval)
-tevent_req_profile_set_status: void (struct tevent_req_profile *, pid_t, enum tevent_req_state, uint64_t)
-tevent_req_profile_set_stop: bool (struct tevent_req_profile *, const char *, struct timeval)
 tevent_req_received: void (struct tevent_req *)
 tevent_req_reset_endtime: void (struct tevent_req *)
 tevent_req_set_callback: void (struct tevent_req *, tevent_req_fn, void *)
@@ -101,7 +76,6 @@ tevent_req_set_cancel_fn: void (struct tevent_req *, tevent_req_cancel_fn)
 tevent_req_set_cleanup_fn: void (struct tevent_req *, tevent_req_cleanup_fn)
 tevent_req_set_endtime: bool (struct tevent_req *, struct tevent_context *, struct timeval)
 tevent_req_set_print_fn: void (struct tevent_req *, tevent_req_print_fn)
-tevent_req_set_profile: bool (struct tevent_req *)
 tevent_sa_info_queue_count: size_t (void)
 tevent_set_abort_fn: void (void (*)(const char *))
 tevent_set_debug: int (struct tevent_context *, void (*)(void *, enum tevent_debug_level, const char *, va_list), void *)
diff --git a/lib/tevent/ABI/tevent-0.9.37.sigs b/lib/tevent/ABI/tevent-0.9.37.sigs
new file mode 100644
index 000000000000..f6227db5c938
--- /dev/null
+++ b/lib/tevent/ABI/tevent-0.9.37.sigs
@@ -0,0 +1,126 @@
+_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 *)
+_tevent_loop_wait: int (struct tevent_context *, const char *)
+_tevent_queue_create: struct tevent_queue *(TALLOC_CTX *, const char *, const char *)
+_tevent_req_callback_data: void *(struct tevent_req *)
+_tevent_req_cancel: bool (struct tevent_req *, const char *)
+_tevent_req_create: struct tevent_req *(TALLOC_CTX *, void *, size_t, const char *, const char *)
+_tevent_req_data: void *(struct tevent_req *)
+_tevent_req_done: void (struct tevent_req *, const char *)
+_tevent_req_error: bool (struct tevent_req *, uint64_t, const char *)
+_tevent_req_nomem: bool (const void *, struct tevent_req *, const char *)
+_tevent_req_notify_callback: void (struct tevent_req *, const char *)
+_tevent_req_oom: void (struct tevent_req *, const char *)
+_tevent_schedule_immediate: void (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *)
+_tevent_threaded_schedule_immediate: void (struct tevent_threaded_context *, struct tevent_immediate *, tevent_immediate_handler_t, void *, const char *, const char *)
+tevent_abort: void (struct tevent_context *, const char *)
+tevent_backend_list: const char **(TALLOC_CTX *)
+tevent_cleanup_pending_signal_handlers: void (struct tevent_signal *)
+tevent_common_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *, int, uint16_t, tevent_fd_handler_t, void *, const char *, const char *)
+tevent_common_add_signal: struct tevent_signal *(struct tevent_context *, TALLOC_CTX *, int, int, tevent_signal_handler_t, void *, const char *, const char *)
+tevent_common_add_timer: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
+tevent_common_add_timer_v2: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
+tevent_common_check_double_free: void (TALLOC_CTX *, const char *)
+tevent_common_check_signal: int (struct tevent_context *)
+tevent_common_context_destructor: int (struct tevent_context *)
+tevent_common_fd_destructor: int (struct tevent_fd *)
+tevent_common_fd_get_flags: uint16_t (struct tevent_fd *)
+tevent_common_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
+tevent_common_fd_set_flags: void (struct tevent_fd *, uint16_t)
+tevent_common_have_events: bool (struct tevent_context *)
+tevent_common_invoke_fd_handler: int (struct tevent_fd *, uint16_t, bool *)
+tevent_common_invoke_immediate_handler: int (struct tevent_immediate *, bool *)
+tevent_common_invoke_signal_handler: int (struct tevent_signal *, int, int, void *, bool *)
+tevent_common_invoke_timer_handler: int (struct tevent_timer *, struct timeval, bool *)
+tevent_common_loop_immediate: bool (struct tevent_context *)
+tevent_common_loop_timer_delay: struct timeval (struct tevent_context *)
+tevent_common_loop_wait: int (struct tevent_context *, const char *)
+tevent_common_schedule_immediate: void (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *)
+tevent_common_threaded_activate_immediate: void (struct tevent_context *)
+tevent_common_wakeup: int (struct tevent_context *)
+tevent_common_wakeup_fd: int (int)
+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 *)
+tevent_fd_set_close_fn: void (struct tevent_fd *, tevent_fd_close_fn_t)
+tevent_fd_set_flags: void (struct tevent_fd *, uint16_t)
+tevent_get_trace_callback: void (struct tevent_context *, tevent_trace_callback_t *, void *)
+tevent_loop_allow_nesting: void (struct tevent_context *)
+tevent_loop_set_nesting_hook: void (struct tevent_context *, tevent_nesting_hook, void *)
+tevent_num_signals: size_t (void)
+tevent_queue_add: bool (struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *)
+tevent_queue_add_entry: struct tevent_queue_entry *(struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *)
+tevent_queue_add_optimize_empty: struct tevent_queue_entry *(struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *)
+tevent_queue_entry_untrigger: void (struct tevent_queue_entry *)
+tevent_queue_length: size_t (struct tevent_queue *)
+tevent_queue_running: bool (struct tevent_queue *)
+tevent_queue_start: void (struct tevent_queue *)
+tevent_queue_stop: void (struct tevent_queue *)
+tevent_queue_wait_recv: bool (struct tevent_req *)
+tevent_queue_wait_send: struct tevent_req *(TALLOC_CTX *, struct tevent_context *, struct tevent_queue *)
+tevent_re_initialise: int (struct tevent_context *)
+tevent_register_backend: bool (const char *, const struct tevent_ops *)
+tevent_req_default_print: char *(struct tevent_req *, TALLOC_CTX *)
+tevent_req_defer_callback: void (struct tevent_req *, struct tevent_context *)
+tevent_req_get_profile: const struct tevent_req_profile *(struct tevent_req *)
+tevent_req_is_error: bool (struct tevent_req *, enum tevent_req_state *, uint64_t *)
+tevent_req_is_in_progress: bool (struct tevent_req *)
+tevent_req_move_profile: struct tevent_req_profile *(struct tevent_req *, TALLOC_CTX *)
+tevent_req_poll: bool (struct tevent_req *, struct tevent_context *)
+tevent_req_post: struct tevent_req *(struct tevent_req *, struct tevent_context *)
+tevent_req_print: char *(TALLOC_CTX *, struct tevent_req *)
+tevent_req_profile_append_sub: void (struct tevent_req_profile *, struct tevent_req_profile **)
+tevent_req_profile_create: struct tevent_req_profile *(TALLOC_CTX *)
+tevent_req_profile_get_name: void (const struct tevent_req_profile *, const char **)
+tevent_req_profile_get_start: void (const struct tevent_req_profile *, const char **, struct timeval *)
+tevent_req_profile_get_status: void (const struct tevent_req_profile *, pid_t *, enum tevent_req_state *, uint64_t *)
+tevent_req_profile_get_stop: void (const struct tevent_req_profile *, const char **, struct timeval *)
+tevent_req_profile_get_subprofiles: const struct tevent_req_profile *(const struct tevent_req_profile *)
+tevent_req_profile_next: const struct tevent_req_profile *(const struct tevent_req_profile *)
+tevent_req_profile_set_name: bool (struct tevent_req_profile *, const char *)
+tevent_req_profile_set_start: bool (struct tevent_req_profile *, const char *, struct timeval)
+tevent_req_profile_set_status: void (struct tevent_req_profile *, pid_t, enum tevent_req_state, uint64_t)
+tevent_req_profile_set_stop: bool (struct tevent_req_profile *, const char *, struct timeval)
+tevent_req_received: void (struct tevent_req *)
+tevent_req_reset_endtime: void (struct tevent_req *)
+tevent_req_set_callback: void (struct tevent_req *, tevent_req_fn, void *)
+tevent_req_set_cancel_fn: void (struct tevent_req *, tevent_req_cancel_fn)
+tevent_req_set_cleanup_fn: void (struct tevent_req *, tevent_req_cleanup_fn)
+tevent_req_set_endtime: bool (struct tevent_req *, struct tevent_context *, struct timeval)
+tevent_req_set_print_fn: void (struct tevent_req *, tevent_req_print_fn)
+tevent_req_set_profile: bool (struct tevent_req *)
+tevent_sa_info_queue_count: size_t (void)
+tevent_set_abort_fn: void (void (*)(const char *))
+tevent_set_debug: int (struct tevent_context *, void (*)(void *, enum tevent_debug_level, const char *, va_list), void *)
+tevent_set_debug_stderr: int (struct tevent_context *)
+tevent_set_default_backend: void (const char *)
+tevent_set_trace_callback: void (struct tevent_context *, tevent_trace_callback_t, void *)
+tevent_signal_support: bool (struct tevent_context *)
+tevent_thread_proxy_create: struct tevent_thread_proxy *(struct tevent_context *)
+tevent_thread_proxy_schedule: void (struct tevent_thread_proxy *, struct tevent_immediate **, tevent_immediate_handler_t, void *)
+tevent_threaded_context_create: struct tevent_threaded_context *(TALLOC_CTX *, struct tevent_context *)
+tevent_timeval_add: struct timeval (const struct timeval *, uint32_t, uint32_t)
+tevent_timeval_compare: int (const struct timeval *, const struct timeval *)
+tevent_timeval_current: struct timeval (void)
+tevent_timeval_current_ofs: struct timeval (uint32_t, uint32_t)
+tevent_timeval_is_zero: bool (const struct timeval *)
+tevent_timeval_set: struct timeval (uint32_t, uint32_t)
+tevent_timeval_until: struct timeval (const struct timeval *, const struct timeval *)
+tevent_timeval_zero: struct timeval (void)
+tevent_trace_point_callback: void (struct tevent_context *, enum tevent_trace_point)
+tevent_update_timer: void (struct tevent_timer *, struct timeval)
+tevent_wakeup_recv: bool (struct tevent_req *)
+tevent_wakeup_send: struct tevent_req *(TALLOC_CTX *, struct tevent_context *, struct timeval)
diff --git a/lib/tevent/wscript b/lib/tevent/wscript
index 2395ead9aa93..54330d27cadb 100644
--- a/lib/tevent/wscript
+++ b/lib/tevent/wscript
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 
 APPNAME = 'tevent'
-VERSION = '0.9.36'
+VERSION = '0.9.37'
 
 blddir = 'bin'
 
-- 
2.17.1


From 1716e501245474e23e79e01325e90e6cbeb1acb3 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 May 2018 13:54:42 +0200
Subject: [PATCH 27/37] lib: Multi-line a long line in wscript_build

Why? I'll add another file in a later commit

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/util/wscript_build | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index ded91f67d9fb..793e24669c95 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -162,7 +162,11 @@ else:
                       )
 
     bld.SAMBA_LIBRARY('tevent-util',
-                      source='tevent_unix.c tevent_ntstatus.c tevent_werror.c',
+                      source='''
+                          tevent_unix.c
+                          tevent_ntstatus.c
+                          tevent_werror.c
+                      ''',
                       local_include=False,
                       public_deps='tevent samba-errors',
                       public_headers='tevent_ntstatus.h tevent_unix.h tevent_werror.h',
-- 
2.17.1


From 77a343ee761456f458bf7be757c2bc22a00a5226 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 May 2018 13:59:57 +0200
Subject: [PATCH 28/37] lib: Add tevent_req_profile helpers

Print and marshall/unmarshall tevent_req_profile structs

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/util/tevent_req_profile.c | 519 ++++++++++++++++++++++++++++++++++
 lib/util/tevent_req_profile.h |  50 ++++
 lib/util/wscript_build        |   1 +
 3 files changed, 570 insertions(+)
 create mode 100644 lib/util/tevent_req_profile.c
 create mode 100644 lib/util/tevent_req_profile.h

diff --git a/lib/util/tevent_req_profile.c b/lib/util/tevent_req_profile.c
new file mode 100644
index 000000000000..522741c5ede8
--- /dev/null
+++ b/lib/util/tevent_req_profile.c
@@ -0,0 +1,519 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Helpers around tevent_req_profile
+ *
+ * Copyright (C) Volker Lendecke 2018
+ *
+ *   ** 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"
+#include <tevent.h>
+#include "lib/util/tevent_req_profile.h"
+#include "lib/util/time_basic.h"
+#include "lib/util/memory.h"
+
+int tevent_req_profile_print(const struct tevent_req_profile *profile,
+			     FILE *fp,
+			     unsigned indent,
+			     unsigned max_indent)
+{
+	struct timeval start, stop, diff;
+	struct timeval_buf start_buf, stop_buf;
+	const char *req_name = NULL;
+	const char *start_location = NULL;
+	const char *stop_location = NULL;
+	pid_t pid;
+	enum tevent_req_state state;
+	const char *state_buf = NULL;
+	uint64_t user_error;
+	const struct tevent_req_profile *sub = NULL;
+	int ret;
+
+	tevent_req_profile_get_name(profile, &req_name);
+
+	tevent_req_profile_get_start(profile, &start_location, &start);
+	timeval_str_buf(&start, false, true, &start_buf);
+
+	tevent_req_profile_get_stop(profile, &stop_location, &stop);
+	timeval_str_buf(&stop, false, true, &stop_buf);
+
+	diff = tevent_timeval_until(&start, &stop);
+
+	tevent_req_profile_get_status(profile, &pid, &state, &user_error);
+
+	switch(state) {
+	case TEVENT_REQ_INIT:
+		state_buf = "TEVENT_REQ_INIT";
+		break;
+	case TEVENT_REQ_IN_PROGRESS:
+		state_buf = "TEVENT_REQ_IN_PROGRESS";
+		break;
+	case TEVENT_REQ_DONE:
+		state_buf = "TEVENT_REQ_DONE";
+		break;
+	case TEVENT_REQ_USER_ERROR:
+		state_buf = "TEVENT_REQ_USER_ERROR";
+		break;
+	case TEVENT_REQ_TIMED_OUT:
+		state_buf = "TEVENT_REQ_TIMED_OUT";
+		break;
+	case TEVENT_REQ_NO_MEMORY:
+		state_buf = "TEVENT_REQ_NO_MEMORY";
+		break;
+	case TEVENT_REQ_RECEIVED:
+		state_buf = "TEVENT_REQ_RECEIVED";
+		break;
+	default:
+		state_buf = "unknown";
+		break;
+	}
+
+	ret = fprintf(
+		fp,
+		"%*s[%s] %s [%s] %s [%s] [%ju.%.6ju] -> %s (%d %"PRIu64"))\n",
+		indent,
+		"",
+		req_name,
+		start_location,
+		start_buf.buf,
+		stop_location,
+		stop_buf.buf,
+		(uintmax_t)diff.tv_sec,
+		(uintmax_t)diff.tv_usec,
+		state_buf,
+		(int)state,
+		user_error);
+
+	if (ret < 0) {
+		return ret;
+	}
+
+	indent += 1;
+
+	if (indent >= max_indent) {
+		return ret;
+	}
+
+	for (sub = tevent_req_profile_get_subprofiles(profile);
+	     sub != NULL;
+	     sub = tevent_req_profile_next(sub)) {
+		int subret;
+
+		subret = tevent_req_profile_print(sub, fp, indent, max_indent);
+		if (subret < 0) {
+			return subret;
+		}
+
+		ret += subret;
+
+		if (ret < subret) {
+			/* overflow */
+			return -1;
+		}
+	}
+
+	return ret;
+}
+
+char *tevent_req_profile_string(const struct tevent_req_profile *profile,
+				TALLOC_CTX *mem_ctx,
+				unsigned indent,
+				unsigned max_indent)
+{
+	FILE *fp = NULL;
+	char *buf = NULL;
+	size_t buflen = 0;
+	char *result = NULL;
+	int ret;
+
+	fp = open_memstream(&buf, &buflen);
+	if (fp == NULL) {
+		return NULL;
+	}
+
+	ret = tevent_req_profile_print(profile, fp, 0, max_indent);
+	if (ret < 0) {
+		goto done;
+	}
+
+	ret = fclose(fp);
+	if (ret != 0) {
+		goto done;
+	}
+
+	/*
+	 * A FILE* from open_memstream maintains the 0-byte at the end
+	 * beyond the reported length.
+	 */
+	result = talloc_memdup(mem_ctx, buf, buflen+1);
+
+done:
+	SAFE_FREE(buf);
+	return result;
+}
+
+static ssize_t tevent_req_profile_pack_one(
+	const struct tevent_req_profile *profile,
+	uint8_t *buf,
+	size_t buflen)
+{
+	const char *req_name = NULL;
+	const char *start_location = NULL;
+	const char *stop_location = NULL;
+	struct timeval start_time, stop_time;
+	pid_t pid;
+	enum tevent_req_state state;
+	uint64_t user_error;
+	size_t pack_len, len;
+	int ret;
+
+	tevent_req_profile_get_name(profile, &req_name);
+	tevent_req_profile_get_start(profile, &start_location, &start_time);
+	tevent_req_profile_get_stop(profile, &stop_location, &stop_time);
+	tevent_req_profile_get_status(profile, &pid, &state, &user_error);
+
+	len = strlen(req_name)+1;
+	if (buflen >= len) {
+		memcpy(buf, req_name, len);
+		buf += len;
+		buflen -= len;
+	}
+
+	pack_len = len;
+
+	len = strlen(start_location)+1;
+	pack_len += len;
+	if (pack_len < len) {
+		return -1;	/* overflow */
+	}
+
+	if (buflen >= len) {
+		memcpy(buf, start_location, len);
+		buf += len;
+		buflen -= len;
+	}
+
+	len = strlen(stop_location)+1;
+	pack_len += len;
+	if (pack_len < len) {
+		return -1;	/* overflow */
+	}
+
+	if (buflen >= len) {
+		memcpy(buf, stop_location, len);
+		buf += len;
+		buflen -= len;
+	}
+
+	ret = snprintf((char *)buf,
+		       buflen,
+		       "%ju %ju %ju %ju %d %d %"PRIu64"",
+		       (uintmax_t)start_time.tv_sec,
+		       (uintmax_t)start_time.tv_usec,
+		       (uintmax_t)stop_time.tv_sec,
+		       (uintmax_t)stop_time.tv_usec,
+		       (int)pid,
+		       (int)state,
+		       user_error);
+	if (ret < 0) {
+		return -1;
+	}
+
+	/*
+	 * Take care of the trailing 0. No overflow check, this would
+	 * be a VERY small number of bits for "int".
+	 */
+	ret += 1;
+
+	pack_len += ret;
+
+	return pack_len;
+}
+
+ssize_t tevent_req_profile_pack(
+	const struct tevent_req_profile *profile,
+	uint8_t *buf,
+	size_t buflen)
+{
+	const struct tevent_req_profile *sub = NULL;
+	size_t num_sub;
+	ssize_t pack_len, profile_len;
+	int ret;
+
+	num_sub = 0;
+	pack_len = 0;
+
+	for (sub = tevent_req_profile_get_subprofiles(profile);
+	     sub != NULL;
+	     sub = tevent_req_profile_next(sub)) {
+		num_sub += 1;
+	}
+
+	ret = snprintf((char *)buf, buflen, "%zu ", num_sub);
+	if (ret < 0) {
+		return -1;
+	}
+
+	if (buflen > (size_t)ret) {
+		buf += ret;
+		buflen -= ret;
+	}
+
+	pack_len = ret;
+
+	profile_len = tevent_req_profile_pack_one(profile, buf, buflen);
+	if (profile_len == -1) {
+		return -1;
+	}
+
+	if (buflen >= (size_t)profile_len) {
+		buf += profile_len;
+		buflen -= profile_len;
+	}
+
+	pack_len += profile_len;
+	if (pack_len < profile_len) {
+		return -1;	/* overflow */
+	}
+
+	for (sub = tevent_req_profile_get_subprofiles(profile);
+	     sub != NULL;
+	     sub = tevent_req_profile_next(sub)) {
+
+		profile_len = tevent_req_profile_pack(sub, buf, buflen);
+		if (profile_len == -1) {
+			return -1;
+		}
+
+		if (buflen >= (size_t)profile_len) {
+			buf += profile_len;
+			buflen -= profile_len;
+		}
+
+		pack_len += profile_len;
+		if (pack_len < profile_len) {
+			return -1;	/* overflow */
+		}
+	}
+
+	return pack_len;
+}
+
+static bool parse_uintmax(const char *buf,
+			  char delimiter,
+			  uintmax_t *presult,
+			  char **p_endptr)
+{
+	uintmax_t result;
+	char *endptr;
+
+	result = strtoumax(buf, &endptr, 10);
+	if ((result == UINTMAX_MAX) && (errno == ERANGE)) {
+		return false;
+	}
+	if (*endptr != delimiter) {
+		return false;
+	}
+
+	*presult = result;
+	*p_endptr = endptr+1;
+
+	return true;
+}
+
+static ssize_t tevent_req_profile_unpack_one(
+	const uint8_t *buf,
+	size_t buflen,
+	struct tevent_req_profile *profile)
+{
+	const char *orig_buf = (const char *)buf;
+	const char *req_name = NULL;
+	const char *start_location = NULL;
+	const char *stop_location = NULL;
+	uintmax_t start_sec, start_usec, stop_sec, stop_usec, pid, state;
+	uintmax_t user_error;
+	char *next = NULL;
+	size_t len;
+	bool ok;
+
+	if (buflen == 0) {
+		return -1;
+	}
+	if (buf[buflen-1] != '\0') {
+		return -1;
+	}
+
+	req_name = (const char *)buf;
+	len = strlen(req_name)+1;
+
+	buf += len;
+	buflen -= len;
+	if (buflen == 0) {
+		return -1;
+	}
+
+	start_location = (const char *)buf;
+	len = strlen(start_location)+1;
+
+	buf += len;
+	buflen -= len;
+	if (buflen == 0) {
+		return -1;
+	}
+
+	stop_location = (const char *)buf;
+	len = strlen(stop_location)+1;
+
+	buf += len;
+	buflen -= len;
+	if (buflen == 0) {
+		return -1;
+	}
+
+	ok = parse_uintmax((const char *)buf, ' ', &start_sec, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, ' ', &start_usec, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, ' ', &stop_sec, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, ' ', &stop_usec, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, ' ', &pid, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, ' ', &state, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = parse_uintmax(next, '\0', &user_error, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = tevent_req_profile_set_name(profile, req_name);
+	if (!ok) {
+		return -1;
+	}
+
+	ok = tevent_req_profile_set_start(
+		profile,
+		start_location,
+		(struct timeval){ .tv_sec=start_sec, .tv_usec=start_usec });
+	if (!ok) {
+		return -1;
+	}
+
+	ok = tevent_req_profile_set_stop(
+		profile,
+		stop_location,
+		(struct timeval){ .tv_sec=stop_sec, .tv_usec=stop_usec });
+	if (!ok) {
+		return -1;
+	}
+
+	tevent_req_profile_set_status(
+		profile,
+		pid,
+		(enum tevent_req_state)state,
+		user_error);
+
+	return next - orig_buf;
+}
+
+ssize_t tevent_req_profile_unpack(
+	const uint8_t *buf,
+	size_t buflen,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_req_profile **p_profile)
+{
+	const uint8_t *orig_buf = buf;
+	struct tevent_req_profile *profile = NULL;
+	uintmax_t i, num_subprofiles;
+	char *next = NULL;
+	bool ok;
+	ssize_t len;
+
+	errno = 0;
+
+	if (buf[buflen-1] != '\0') {
+		return -1;
+	}
+
+	ok = parse_uintmax((const char *)buf, ' ', &num_subprofiles, &next);
+	if (!ok) {
+		return -1;
+	}
+
+	len = (next - (const char *)buf);
+
+	buf += len;
+	buflen -= len;
+
+	profile = tevent_req_profile_create(mem_ctx);
+	if (profile == NULL) {
+		return -1;
+	}
+
+	len = tevent_req_profile_unpack_one(buf, buflen, profile);
+	if (len == -1) {
+		TALLOC_FREE(profile);
+		return -1;
+	}
+
+	buf += len;
+	buflen -= len;
+
+	for (i=0; i<num_subprofiles; i++) {
+		struct tevent_req_profile *subprofile;
+
+		len = tevent_req_profile_unpack(
+			buf,
+			buflen,
+			profile,
+			&subprofile);
+		if (len == -1) {
+			TALLOC_FREE(profile);
+			return -1;
+		}
+		buf += len;
+		buflen -= len;
+
+		tevent_req_profile_append_sub(profile, &subprofile);
+	}
+
+	*p_profile = profile;
+
+	return buf - orig_buf;
+}
diff --git a/lib/util/tevent_req_profile.h b/lib/util/tevent_req_profile.h
new file mode 100644
index 000000000000..00dbc5a9cc20
--- /dev/null
+++ b/lib/util/tevent_req_profile.h
@@ -0,0 +1,50 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Helpers around tevent_req_profile
+ *
+ * Copyright (C) Volker Lendecke 2018
+ *
+ *   ** 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/>.
+ */
+
+#ifndef __LIB_UTIL_TEVENT_REQ_PROFILE_UNPACK
+#define __LIB_UTIL_TEVENT_REQ_PROFILE_UNPACK
+
+#include "replace.h"
+#include <tevent.h>
+
+int tevent_req_profile_print(const struct tevent_req_profile *profile,
+			     FILE *fp,
+			     unsigned indent,
+			     unsigned max_indent);
+char *tevent_req_profile_string(const struct tevent_req_profile *profile,
+				TALLOC_CTX *mem_ctx,
+				unsigned indent,
+				unsigned max_indent);
+ssize_t tevent_req_profile_pack(
+	const struct tevent_req_profile *profile,
+	uint8_t *buf,
+	size_t buflen);
+ssize_t tevent_req_profile_unpack(
+	const uint8_t *buf,
+	size_t buflen,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_req_profile **p_profile);
+
+#endif
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index 793e24669c95..8fc402062fbd 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -166,6 +166,7 @@ else:
                           tevent_unix.c
                           tevent_ntstatus.c
                           tevent_werror.c
+                          tevent_req_profile.c
                       ''',
                       local_include=False,
                       public_deps='tevent samba-errors',
-- 
2.17.1


From 8af878e2c5154edb5696d463b1f879bf42f014a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 2 May 2018 14:02:18 +0200
Subject: [PATCH 29/37] torture: Test tevent_req_profile

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/tevent/test_req.c               | 277 ++++++++++++++++++++++++++++
 source4/torture/local/local.c       |   1 +
 source4/torture/local/wscript_build |   1 +
 3 files changed, 279 insertions(+)
 create mode 100644 lib/tevent/test_req.c

diff --git a/lib/tevent/test_req.c b/lib/tevent/test_req.c
new file mode 100644
index 000000000000..565ef31024fb
--- /dev/null
+++ b/lib/tevent/test_req.c
@@ -0,0 +1,277 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * testing of some tevent_req aspects
+ *
+ * Copyright (C) Volker Lendecke 2018
+ *
+ *   ** 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 "includes.h"
+#include "tevent.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_req_profile.h"
+#include "lib/util/time_basic.h"
+
+struct tevent_req_create_state {
+	uint8_t val;
+};
+
+static bool test_tevent_req_create(struct torture_context *tctx,
+				   const void *test_data)
+{
+	struct tevent_req *req;
+	struct tevent_req_create_state *state;
+
+	req = tevent_req_create(tctx, &state,
+				struct tevent_req_create_state);
+	torture_assert_not_null(tctx, req, "tevent_req_create failed\n");
+	torture_assert_int_equal(tctx, state->val, 0, "state not initialized\n");
+
+	TALLOC_FREE(req);
+
+	return true;
+}
+
+struct profile1_state {
+	uint8_t dummy;
+};
+
+static bool test_tevent_req_profile1(struct torture_context *tctx,
+				     const void *test_data)
+{
+	struct tevent_req *req;
+	struct profile1_state *state;
+	const struct tevent_req_profile *p1;
+	struct tevent_req_profile *p2;
+	struct timeval start, stop;
+	bool ok;
+	int cmp;
+
+	req = tevent_req_create(tctx, &state, struct profile1_state);
+	torture_assert_not_null(tctx, req, "tevent_req_create failed\n");
+
+	p1 = tevent_req_get_profile(req);
+	torture_assert(tctx, p1 == NULL, "profile not initialized to NULL\n");
+
+	ok = tevent_req_set_profile(req);
+	torture_assert(tctx, ok, "set_profile failed\n");
+
+	tevent_req_done(req);
+
+	p2 = tevent_req_move_profile(req, tctx);
+	torture_assert_not_null(tctx, p2, "get_profile failed\n");
+
+	/* Demonstrate sure "p2" outlives req */
+	TALLOC_FREE(req);
+
+	tevent_req_profile_get_start(p2, NULL, &start);
+	tevent_req_profile_get_stop(p2, NULL, &stop);
+
+	cmp = tevent_timeval_compare(&start, &stop);
+	torture_assert(tctx, cmp <= 0, "stop before start\n");
+
+	TALLOC_FREE(p2);
+
+	return true;
+}
+
+struct profile2_state {
+	uint8_t dummy;
+};
+
+static void profile2_done(struct tevent_req *subreq);
+
+static struct tevent_req *profile2_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev)
+{
+	struct tevent_req *req, *subreq;
+	struct profile2_state *state;
+	bool ok;
+
+	req = tevent_req_create(mem_ctx, &state, struct profile2_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	ok = tevent_req_set_profile(req);
+	if (!ok) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = tevent_wakeup_send(
+		state,
+		ev,
+		tevent_timeval_current_ofs(0, 1));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, profile2_done, req);
+
+	return req;
+}
+
+static void profile2_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	bool ok;
+
+	ok = tevent_wakeup_recv(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static int profile2_recv(struct tevent_req *req,
+			  TALLOC_CTX *mem_ctx,
+			  struct tevent_req_profile **profile)
+{
+	int err;
+
+	if (tevent_req_is_unix_error(req, &err)) {
+		return err;
+	}
+
+	*profile = tevent_req_move_profile(req, mem_ctx);
+
+	return 0;
+}
+
+static bool test_tevent_req_profile2(struct torture_context *tctx,
+				     const void *test_data)
+{
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	struct tevent_req_profile *p1 = NULL;
+	struct tevent_req_profile *p2 = NULL;
+	const char *str1, *str2;
+	struct timeval tv1, tv2;
+	pid_t pid1, pid2;
+	enum tevent_req_state state1, state2;
+	uint64_t err1, err2;
+	ssize_t pack_len;
+	int err;
+	bool ok;
+
+	ev = samba_tevent_context_init(tctx);
+	torture_assert_not_null(tctx, ev, "samba_tevent_context_init failed\n");
+
+	req = profile2_send(tctx, ev);
+	torture_assert_not_null(tctx, req, "profile2_send failed\n");
+
+	ok = tevent_req_poll_unix(req, ev, &err);
+	torture_assert(tctx, ok, "tevent_req_poll_unix failed\n");
+
+	err = profile2_recv(req, tctx, &p1);
+	torture_assert_int_equal(tctx, err, 0, "profile2_recv failed\n");
+
+	TALLOC_FREE(req);
+	TALLOC_FREE(ev);
+
+	tevent_req_profile_print(p1, stdout, 0, UINT_MAX);
+
+	pack_len = tevent_req_profile_pack(p1, NULL, 0);
+	torture_assert(tctx, pack_len>0, "profile_pack failed\n");
+
+	{
+		uint8_t buf[pack_len];
+		ssize_t unpack_len;
+
+		tevent_req_profile_pack(p1, buf, sizeof(buf));
+		dump_data(10, buf, sizeof(buf));
+
+		unpack_len = tevent_req_profile_unpack(
+			buf,
+			pack_len,
+			tctx,
+			&p2);
+		torture_assert_int_equal(tctx,
+					 pack_len,
+					 unpack_len,
+					 "profile_unpack failed\n");
+	}
+
+	tevent_req_profile_print(p2, stdout, 0, UINT_MAX);
+
+	tevent_req_profile_get_name(p1, &str1);
+	tevent_req_profile_get_name(p2, &str2);
+	torture_assert_str_equal(tctx, str1, str2, "names differ\n");
+
+	tevent_req_profile_get_start(p1, &str1, &tv1);
+	tevent_req_profile_get_start(p2, &str2, &tv2);
+	torture_assert_str_equal(tctx, str1, str2, "start strings differ\n");
+	torture_assert(tctx,
+		       tevent_timeval_compare(&tv1, &tv2) == 0,
+		       "start times differ\n");
+
+	tevent_req_profile_get_stop(p1, &str1, &tv1);
+	tevent_req_profile_get_stop(p2, &str2, &tv2);
+	torture_assert_str_equal(tctx, str1, str2, "stop strings differ\n");
+	torture_assert(tctx,
+		       tevent_timeval_compare(&tv1, &tv2) == 0,
+		       "stop times differ\n");
+
+	tevent_req_profile_get_status(p1, &pid1, &state1, &err1);
+	tevent_req_profile_get_status(p2, &pid2, &state2, &err2);
+	torture_assert_int_equal(tctx, pid1, pid2, "pids differ\n");
+	torture_assert_int_equal(tctx, state1, state2, "states differ\n");
+	torture_assert_int_equal(tctx, err1, err2, "user errors differ\n");
+
+	str1 = tevent_req_profile_string(p1, p1, 0, UINT_MAX);
+	torture_assert_not_null(tctx, str1, "profile_string failed\n");
+	str2 = tevent_req_profile_string(p2, p2, 0, UINT_MAX);
+	torture_assert_not_null(tctx, str2, "profile_string failed\n");
+
+	torture_assert_str_equal(tctx, str1, str2, "result strings differ\n");
+
+	TALLOC_FREE(p1);
+	TALLOC_FREE(p2);
+
+	return true;
+}
+
+struct torture_suite *torture_local_tevent_req(TALLOC_CTX *mem_ctx)
+{
+	struct torture_suite *suite;
+
+	suite = torture_suite_create(mem_ctx, "tevent_req");
+
+	torture_suite_add_simple_tcase_const(
+		suite,
+		"create",
+		test_tevent_req_create,
+		NULL);
+	torture_suite_add_simple_tcase_const(
+		suite,
+		"profile1",
+		test_tevent_req_profile1,
+		NULL);
+	torture_suite_add_simple_tcase_const(
+		suite,
+		"profile2",
+		test_tevent_req_profile2,
+		NULL);
+
+	return suite;
+}
diff --git a/source4/torture/local/local.c b/source4/torture/local/local.c
index d1c523bdc1f6..69ecc6b6ec81 100644
--- a/source4/torture/local/local.c
+++ b/source4/torture/local/local.c
@@ -64,6 +64,7 @@
 	torture_local_string_case,
 	torture_local_compression,
 	torture_local_event, 
+	torture_local_tevent_req,
 	torture_local_torture,
 	torture_local_dbspeed, 
 	torture_local_credentials,
diff --git a/source4/torture/local/wscript_build b/source4/torture/local/wscript_build
index 6cbb14d86e48..2dd18193c94d 100644
--- a/source4/torture/local/wscript_build
+++ b/source4/torture/local/wscript_build
@@ -13,6 +13,7 @@ TORTURE_LOCAL_SOURCE = '''../../../lib/util/charset/tests/iconv.c
         ../../../lib/util/charset/tests/convert_string.c
 	../../libcli/security/tests/sddl.c ../../../lib/tdr/testsuite.c
 	../../../lib/tevent/testsuite.c ../../param/tests/share.c
+        ../../../lib/tevent/test_req.c
 	../../param/tests/loadparm.c ../../../auth/credentials/tests/simple.c local.c
 	dbspeed.c torture.c ../ldb/ldb.c ../../dsdb/common/tests/dsdb_dn.c
 	../../dsdb/schema/tests/schema_syntax.c
-- 
2.17.1


From bcc268270f2c9c79af0a7e3412e46446614b4a24 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 3 May 2018 15:12:55 +0200
Subject: [PATCH 30/37] winbindd: Convert process_request() to tevent_req

Having a central tevent_req per winbind child request is prerequisite
for request profiling

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/winbindd/winbindd.c | 328 ++++++++++++++++++++----------------
 1 file changed, 181 insertions(+), 147 deletions(-)

diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
index f0f0eef7bfc2..56c5b6ed8e2e 100644
--- a/source3/winbindd/winbindd.c
+++ b/source3/winbindd/winbindd.c
@@ -54,8 +54,6 @@
 static bool client_is_idle(struct winbindd_cli_state *state);
 static void remove_client(struct winbindd_cli_state *state);
 static void winbindd_setup_max_fds(void);
-static void request_ok(struct winbindd_cli_state *state);
-static void request_error(struct winbindd_cli_state *state);
 
 static bool opt_nocache = False;
 static bool interactive = False;
@@ -660,219 +658,222 @@ static struct winbindd_async_dispatch_table async_priv_table[] = {
 	{ 0, NULL, NULL, NULL }
 };
 
-static void wb_request_done(struct tevent_req *req);
+struct process_request_state {
+	struct winbindd_cli_state *cli_state;
+	struct tevent_context *ev;
+};
 
-static void process_request(struct winbindd_cli_state *state)
+static void process_request_done(struct tevent_req *subreq);
+static void process_request_written(struct tevent_req *subreq);
+
+static struct tevent_req *process_request_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct winbindd_cli_state *cli_state)
 {
+	struct tevent_req *req, *subreq;
+	struct process_request_state *state;
 	struct winbindd_async_dispatch_table *atable;
+	enum winbindd_cmd cmd = cli_state->request->cmd;
 	size_t i;
 	bool ok;
 
-	state->mem_ctx = talloc_named(state, 0, "winbind request");
-	if (state->mem_ctx == NULL)
-		return;
+	req = tevent_req_create(mem_ctx, &state,
+				struct process_request_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->cli_state = cli_state;
+	state->ev = ev;
+
+	SMB_ASSERT(cli_state->mem_ctx == NULL);
+	cli_state->mem_ctx = talloc_named(cli_state, 0, "winbind request");
+	if (tevent_req_nomem(cli_state->mem_ctx, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	cli_state->response = talloc_zero(
+		cli_state->mem_ctx,
+		struct winbindd_response);
+	if (tevent_req_nomem(cli_state->response, req)) {
+		return tevent_req_post(req, ev);
+	}
+	cli_state->response->result = WINBINDD_PENDING;
+	cli_state->response->length = sizeof(struct winbindd_response);
 
 	/* Remember who asked us. */
-	state->pid = state->request->pid;
+	cli_state->pid = cli_state->request->pid;
 
-	state->cmd_name = "unknown request";
-	state->recv_fn = NULL;
-	/* client is newest */
-	winbindd_promote_client(state);
+	cli_state->cmd_name = "unknown request";
+	cli_state->recv_fn = NULL;
 
-	/* Process command */
+	/* client is newest */
+	winbindd_promote_client(cli_state);
 
 	for (atable = async_nonpriv_table; atable->send_req; atable += 1) {
-		if (state->request->cmd == atable->cmd) {
+		if (cmd == atable->cmd) {
 			break;
 		}
 	}
 
-	if ((atable->send_req == NULL) && state->privileged) {
+	if ((atable->send_req == NULL) && cli_state->privileged) {
 		for (atable = async_priv_table; atable->send_req;
 		     atable += 1) {
-			if (state->request->cmd == atable->cmd) {
+			if (cmd == atable->cmd) {
 				break;
 			}
 		}
 	}
 
 	if (atable->send_req != NULL) {
-		struct tevent_req *req;
-
-		state->cmd_name = atable->cmd_name;
-		state->recv_fn = atable->recv_req;
+		cli_state->cmd_name = atable->cmd_name;
+		cli_state->recv_fn = atable->recv_req;
 
 		DEBUG(10, ("process_request: Handling async request %d:%s\n",
-			   (int)state->pid, state->cmd_name));
-
-		req = atable->send_req(state->mem_ctx, server_event_context(),
-				       state, state->request);
-		if (req == NULL) {
-			DEBUG(0, ("process_request: atable->send failed for "
-				  "%s\n", atable->cmd_name));
-			request_error(state);
-			return;
+			   (int)cli_state->pid, cli_state->cmd_name));
+
+		subreq = atable->send_req(
+			state,
+			state->ev,
+			cli_state,
+			cli_state->request);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
 		}
-		tevent_req_set_callback(req, wb_request_done, state);
-		return;
+		tevent_req_set_callback(subreq, process_request_done, req);
+		return req;
 	}
 
-	state->response = talloc_zero(state->mem_ctx,
-				      struct winbindd_response);
-	if (state->response == NULL) {
-		DEBUG(10, ("talloc failed\n"));
-		remove_client(state);
-		return;
-	}
-	state->response->result = WINBINDD_PENDING;
-	state->response->length = sizeof(struct winbindd_response);
-
 	for (i=0; i<ARRAY_SIZE(bool_dispatch_table); i++) {
-		if (bool_dispatch_table[i].cmd == state->request->cmd) {
+		if (cmd == bool_dispatch_table[i].cmd) {
 			break;
 		}
 	}
 
-	if (i == ARRAY_SIZE(bool_dispatch_table)) {
-		DEBUG(10,("process_request: unknown request fn number %d\n",
-			  (int)state->request->cmd ));
-		request_error(state);
-		return;
-	}
-
-	DBG_DEBUG("process_request: request fn %s\n",
-		  bool_dispatch_table[i].cmd_name);
-
-	ok = bool_dispatch_table[i].fn(state);
+	ok = false;
 
-	if (ok) {
-		request_ok(state);
-	} else {
-		request_error(state);
+	if (i < ARRAY_SIZE(bool_dispatch_table)) {
+		DBG_DEBUG("process_request: request fn %s\n",
+			  bool_dispatch_table[i].cmd_name);
+		ok = bool_dispatch_table[i].fn(cli_state);
 	}
-}
 
-static void wb_request_done(struct tevent_req *req)
-{
-	struct winbindd_cli_state *state = tevent_req_callback_data(
-		req, struct winbindd_cli_state);
-	NTSTATUS status;
+	cli_state->response->result = ok ? WINBINDD_OK : WINBINDD_ERROR;
 
-	state->response = talloc_zero(state->mem_ctx,
-				      struct winbindd_response);
-	if (state->response == NULL) {
-		DEBUG(0, ("wb_request_done[%d:%s]: talloc_zero failed - removing client\n",
-			  (int)state->pid, state->cmd_name));
-		remove_client(state);
-		return;
-	}
-	state->response->result = WINBINDD_PENDING;
-	state->response->length = sizeof(struct winbindd_response);
+	TALLOC_FREE(cli_state->io_req);
+	TALLOC_FREE(cli_state->request);
 
-	status = state->recv_fn(req, state->response);
-	TALLOC_FREE(req);
+	subreq = wb_resp_write_send(
+		state,
+		state->ev,
+		cli_state->out_queue,
+		cli_state->sock,
+		cli_state->response);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, process_request_written, req);
 
-	DEBUG(10,("wb_request_done[%d:%s]: %s\n",
-		  (int)state->pid, state->cmd_name, nt_errstr(status)));
+	cli_state->io_req = subreq;
 
-	if (!NT_STATUS_IS_OK(status)) {
-		request_error(state);
-		return;
-	}
-	request_ok(state);
+	return req;
 }
 
-/*
- * This is the main event loop of winbind requests. It goes through a
- * state-machine of 3 read/write requests, 4 if you have extra data to send.
- *
- * An idle winbind client has a read request of 4 bytes outstanding,
- * finalizing function is request_len_recv, checking the length. request_recv
- * then processes the packet. The processing function then at some point has
- * to call request_finished which schedules sending the response.
- */
-
-static void request_finished(struct winbindd_cli_state *state);
+static void process_request_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct process_request_state *state = tevent_req_data(
+		req, struct process_request_state);
+	struct winbindd_cli_state *cli_state = state->cli_state;
+	NTSTATUS status;
+	bool ok;
 
-static void winbind_client_request_read(struct tevent_req *req);
-static void winbind_client_response_written(struct tevent_req *req);
-static void winbind_client_activity(struct tevent_req *req);
+	status = cli_state->recv_fn(subreq, cli_state->response);
+	TALLOC_FREE(subreq);
 
-static void request_finished(struct winbindd_cli_state *state)
-{
-	struct tevent_req *req;
+	DBG_DEBUG("[%d:%s]: %s\n",
+		  (int)cli_state->pid,
+		  cli_state->cmd_name,
+		  nt_errstr(status));
 
-	/* free client socket monitoring request */
-	TALLOC_FREE(state->io_req);
+	ok = NT_STATUS_IS_OK(status);
+	cli_state->response->result = ok ? WINBINDD_OK : WINBINDD_ERROR;
 
-	TALLOC_FREE(state->request);
+	TALLOC_FREE(cli_state->io_req);
+	TALLOC_FREE(cli_state->request);
 
-	req = wb_resp_write_send(state, server_event_context(),
-				 state->out_queue, state->sock,
-				 state->response);
-	if (req == NULL) {
-		DEBUG(10,("request_finished[%d:%s]: wb_resp_write_send() failed\n",
-			  (int)state->pid, state->cmd_name));
-		remove_client(state);
+	subreq = wb_resp_write_send(
+		state,
+		state->ev,
+		cli_state->out_queue,
+		cli_state->sock,
+		cli_state->response);
+	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
-	tevent_req_set_callback(req, winbind_client_response_written, state);
-	state->io_req = req;
+	tevent_req_set_callback(subreq, process_request_written, req);
+
+	cli_state->io_req = subreq;
 }
 
-static void winbind_client_response_written(struct tevent_req *req)
+static void process_request_written(struct tevent_req *subreq)
 {
-	struct winbindd_cli_state *state = tevent_req_callback_data(
-		req, struct winbindd_cli_state);
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct process_request_state *state = tevent_req_data(
+		req, struct process_request_state);
+	struct winbindd_cli_state *cli_state = state->cli_state;
 	ssize_t ret;
 	int err;
 
-	state->io_req = NULL;
+	cli_state->io_req = NULL;
 
-	ret = wb_resp_write_recv(req, &err);
-	TALLOC_FREE(req);
+	ret = wb_resp_write_recv(subreq, &err);
+	TALLOC_FREE(subreq);
 	if (ret == -1) {
-		close(state->sock);
-		state->sock = -1;
-		DEBUG(2, ("Could not write response[%d:%s] to client: %s\n",
-			  (int)state->pid, state->cmd_name, strerror(err)));
-		remove_client(state);
+		tevent_req_nterror(req, map_nt_error_from_unix(err));
 		return;
 	}
 
-	DEBUG(10,("winbind_client_response_written[%d:%s]: delivered response "
-		  "to client\n", (int)state->pid, state->cmd_name));
+	DBG_DEBUG("[%d:%s]: delivered response to client\n",
+		  (int)cli_state->pid, cli_state->cmd_name);
 
-	TALLOC_FREE(state->mem_ctx);
-	state->response = NULL;
-	state->cmd_name = "no request";
-	state->recv_fn = NULL;
+	TALLOC_FREE(cli_state->mem_ctx);
+	cli_state->response = NULL;
+	cli_state->cmd_name = "no request";
+	cli_state->recv_fn = NULL;
 
-	req = wb_req_read_send(state, server_event_context(), state->sock,
-			       WINBINDD_MAX_EXTRA_DATA);
-	if (req == NULL) {
-		remove_client(state);
-		return;
-	}
-	tevent_req_set_callback(req, winbind_client_request_read, state);
-	state->io_req = req;
+	tevent_req_done(req);
 }
 
-static void request_error(struct winbindd_cli_state *state)
+static NTSTATUS process_request_recv(struct tevent_req *req)
 {
-	SMB_ASSERT(state->response->result == WINBINDD_PENDING);
-	state->response->result = WINBINDD_ERROR;
-	request_finished(state);
-}
+	NTSTATUS status;
 
-static void request_ok(struct winbindd_cli_state *state)
-{
-	SMB_ASSERT(state->response->result == WINBINDD_PENDING);
-	state->response->result = WINBINDD_OK;
-	request_finished(state);
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
 }
 
+/*
+ * This is the main event loop of winbind requests. It goes through a
+ * state-machine of 3 read/write requests, 4 if you have extra data to send.
+ *
+ * An idle winbind client has a read request of 4 bytes outstanding,
+ * finalizing function is request_len_recv, checking the length. request_recv
+ * then processes the packet. The processing function then at some point has
+ * to call request_finished which schedules sending the response.
+ */
+
+static void winbind_client_request_read(struct tevent_req *req);
+static void winbind_client_activity(struct tevent_req *req);
+static void winbind_client_processed(struct tevent_req *req);
+
 /* Process a new connection by adding it to the client connection list */
 
 static void new_connection(int listen_sock, bool privileged)
@@ -970,7 +971,13 @@ static void winbind_client_request_read(struct tevent_req *req)
 	tevent_req_set_callback(req, winbind_client_activity, state);
 	state->io_req = req;
 
-	process_request(state);
+	req = process_request_send(state, server_event_context(), state);
+	if (req == NULL) {
+		DBG_ERR("process_request_send failed\n");
+		remove_client(state);
+		return;
+	}
+	tevent_req_set_callback(req, winbind_client_processed, state);
 }
 
 static void winbind_client_activity(struct tevent_req *req)
@@ -1004,6 +1011,33 @@ static void winbind_client_activity(struct tevent_req *req)
 	remove_client(state);
 }
 
+static void winbind_client_processed(struct tevent_req *req)
+{
+	struct winbindd_cli_state *cli_state = tevent_req_callback_data(
+		req, struct winbindd_cli_state);
+	NTSTATUS status;
+
+	status = process_request_recv(req);
+	TALLOC_FREE(req);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_DEBUG("process_request failed: %s\n", nt_errstr(status));
+		remove_client(cli_state);
+		return;
+	}
+
+	req = wb_req_read_send(
+		cli_state,
+		server_event_context(),
+		cli_state->sock,
+		WINBINDD_MAX_EXTRA_DATA);
+	if (req == NULL) {
+		remove_client(cli_state);
+		return;
+	}
+	tevent_req_set_callback(req, winbind_client_request_read, cli_state);
+	cli_state->io_req = req;
+}
+
 /* Remove a client connection from client connection list */
 
 static void remove_client(struct winbindd_cli_state *state)
-- 
2.17.1


From 6329e00acee3ff4c40828fed0ffec13c3adbd716 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 19 Jun 2018 11:13:19 +0200
Subject: [PATCH 31/37] winbindd: Do request profiling

By default we log a request that takes more than 60 seconds. This can be
changed by setting

winbind:request profile threshold = <seconds>

Another parameter controls the depth of the request hierarchy printed:

winbind:request profile depth = <n>

The default request logged to syslog via DEBUG(0) looks like the
following for a wbinfo -P:

[struct process_request_state] ../source3/winbindd/winbindd.c:683 [2018/06/19 13:33:14.190365] ../source3/winbindd/winbindd.c:853 [2018/06/19 13:33:14.192737] [0.002372] -> TEVENT_REQ_DONE (2 0))
 [struct winbindd_ping_dc_state] ../source3/winbindd/winbindd_ping_dc.c:41 [2018/06/19 13:33:14.190369] ../source3/winbindd/winbindd_ping_dc.c:112 [2018/06/19 13:33:14.192681] [0.002312] -> TEVENT_REQ_DONE (2 0))
  [struct dcerpc_wbint_PingDc_state] default/librpc/gen_ndr/ndr_winbind_c.c:4335 [2018/06/19 13:33:14.190383] default/librpc/gen_ndr/ndr_winbind_c.c:4396 [2018/06/19 13:33:14.192680] [0.002297] -> TEVENT_REQ_DONE (2 0))
   [struct dcerpc_wbint_PingDc_r_state] default/librpc/gen_ndr/ndr_winbind_c.c:4251 [2018/06/19 13:33:14.190385] default/librpc/gen_ndr/ndr_winbind_c.c:4285 [2018/06/19 13:33:14.192678] [0.002293] -> TEVENT_REQ_DONE (2 0))
    [struct dcerpc_binding_handle_call_state] ../librpc/rpc/binding_handle.c:371 [2018/06/19 13:33:14.190387] ../librpc/rpc/binding_handle.c:520 [2018/06/19 13:33:14.192675] [0.002288] -> TEVENT_REQ_DONE (2 0))
     [struct dcerpc_binding_handle_raw_call_state] ../librpc/rpc/binding_handle.c:149 [2018/06/19 13:33:14.190400] ../librpc/rpc/binding_handle.c:203 [2018/06/19 13:33:14.192646] [0.002246] -> TEVENT_REQ_DONE (2 0))
      [struct wbint_bh_raw_call_state] ../source3/winbindd/winbindd_dual_ndr.c:89 [2018/06/19 13:33:14.190402] ../source3/winbindd/winbindd_dual_ndr.c:204 [2018/06/19 13:33:14.192644] [0.002242] -> TEVENT_REQ_DONE (2 0))
       [struct wb_domain_request_state] ../source3/winbindd/winbindd_dual.c:473 [2018/06/19 13:33:14.190404] ../source3/winbindd/winbindd_dual.c:708 [2018/06/19 13:33:14.192640] [0.002236] -> TEVENT_REQ_DONE (2 0))
        [struct wb_child_request_state] ../source3/winbindd/winbindd_dual.c:198 [2018/06/19 13:33:14.190411] ../source3/winbindd/winbindd_dual.c:273 [2018/06/19 13:33:14.192638] [0.002227] -> TEVENT_REQ_DONE (2 0))
         [struct tevent_queue_wait_state] ../lib/tevent/tevent_queue.c:336 [2018/06/19 13:33:14.190412] ../lib/tevent/tevent_queue.c:355 [2018/06/19 13:33:14.190415] [0.000003] -> TEVENT_REQ_DONE (2 0))
         [struct wb_simple_trans_state] ../nsswitch/wb_reqtrans.c:375 [2018/06/19 13:33:14.190424] ../nsswitch/wb_reqtrans.c:432 [2018/06/19 13:33:14.192630] [0.002206] -> TEVENT_REQ_DONE (2 0))
          [struct req_write_state] ../nsswitch/wb_reqtrans.c:158 [2018/06/19 13:33:14.190425] ../nsswitch/wb_reqtrans.c:194 [2018/06/19 13:33:14.190472] [0.000047] -> TEVENT_REQ_DONE (2 0))
           [struct writev_state] ../lib/async_req/async_sock.c:263 [2018/06/19 13:33:14.190432] ../lib/async_req/async_sock.c:412 [2018/06/19 13:33:14.190470] [0.000038] -> TEVENT_REQ_DONE (2 0))
          [struct resp_read_state] ../nsswitch/wb_reqtrans.c:222 [2018/06/19 13:33:14.190475] ../nsswitch/wb_reqtrans.c:275 [2018/06/19 13:33:14.192629] [0.002154] -> TEVENT_REQ_DONE (2 0))
           [struct read_packet_state] ../lib/async_req/async_sock.c:458 [2018/06/19 13:33:14.190476] ../lib/async_req/async_sock.c:546 [2018/06/19 13:33:14.192626] [0.002150] -> TEVENT_REQ_DONE (2 0))
 [struct resp_write_state] ../nsswitch/wb_reqtrans.c:307 [2018/06/19 13:33:14.192693] ../nsswitch/wb_reqtrans.c:344 [2018/06/19 13:33:14.192734] [0.000041] -> TEVENT_REQ_DONE (2 0))
  [struct writev_state] ../lib/async_req/async_sock.c:263 [2018/06/19 13:33:14.192694] ../lib/async_req/async_sock.c:412 [2018/06/19 13:33:14.192732] [0.000038] -> TEVENT_REQ_DONE (2 0))

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/winbindd/winbindd.c | 46 +++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
index 56c5b6ed8e2e..254d93b344dd 100644
--- a/source3/winbindd/winbindd.c
+++ b/source3/winbindd/winbindd.c
@@ -45,6 +45,7 @@
 #include "libsmb/samlogon_cache.h"
 #include "libcli/auth/netlogon_creds_cli.h"
 #include "passdb.h"
+#include "lib/util/tevent_req_profile.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -686,6 +687,11 @@ static struct tevent_req *process_request_send(
 	state->cli_state = cli_state;
 	state->ev = ev;
 
+	ok = tevent_req_set_profile(req);
+	if (!ok) {
+		return tevent_req_post(req, ev);
+	}
+
 	SMB_ASSERT(cli_state->mem_ctx == NULL);
 	cli_state->mem_ctx = talloc_named(cli_state, 0, "winbind request");
 	if (tevent_req_nomem(cli_state->mem_ctx, req)) {
@@ -847,7 +853,10 @@ static void process_request_written(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS process_request_recv(struct tevent_req *req)
+static NTSTATUS process_request_recv(
+	struct tevent_req *req,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_req_profile **profile)
 {
 	NTSTATUS status;
 
@@ -856,6 +865,7 @@ static NTSTATUS process_request_recv(struct tevent_req *req)
 		return status;
 	}
 
+	*profile = tevent_req_move_profile(req, mem_ctx);
 	tevent_req_received(req);
 	return NT_STATUS_OK;
 }
@@ -1015,9 +1025,12 @@ static void winbind_client_processed(struct tevent_req *req)
 {
 	struct winbindd_cli_state *cli_state = tevent_req_callback_data(
 		req, struct winbindd_cli_state);
+	struct tevent_req_profile *profile = NULL;
+	struct timeval start, stop, diff;
+	int threshold;
 	NTSTATUS status;
 
-	status = process_request_recv(req);
+	status = process_request_recv(req, cli_state, &profile);
 	TALLOC_FREE(req);
 	if (!NT_STATUS_IS_OK(status)) {
 		DBG_DEBUG("process_request failed: %s\n", nt_errstr(status));
@@ -1025,6 +1038,35 @@ static void winbind_client_processed(struct tevent_req *req)
 		return;
 	}
 
+	tevent_req_profile_get_start(profile, NULL, &start);
+	tevent_req_profile_get_stop(profile, NULL, &stop);
+	diff = tevent_timeval_until(&start, &stop);
+
+	threshold = lp_parm_int(-1, "winbind", "request profile threshold", 60);
+
+	if (diff.tv_sec >= threshold) {
+		int depth;
+		char *str;
+
+		depth = lp_parm_int(
+			-1,
+			"winbind",
+			"request profile depth",
+			INT_MAX);
+
+		DBG_ERR("request took %u.%.6u seconds\n",
+			(unsigned)diff.tv_sec, (unsigned)diff.tv_usec);
+
+		str = tevent_req_profile_string(profile, talloc_tos(), 0, depth);
+		if (str != NULL) {
+			/* No "\n", already contained in "str" */
+			DEBUGADD(0, ("%s", str));
+		}
+		TALLOC_FREE(str);
+	}
+
+	TALLOC_FREE(profile);
+
 	req = wb_req_read_send(
 		cli_state,
 		server_event_context(),
-- 
2.17.1


From 4043821c684613ac74baef98b5c1eb335bbf1ac5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 18 May 2018 16:28:47 +0200
Subject: [PATCH 32/37] s3:messages: protect against usage of wrapper
 tevent_context objects for messaging

This makes a lot of assumtion easier to understand and the introduction
of wrapper tevent contexts will not change the existing behaviour.

We'll relax this a bit in the next commits.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/lib/messages.c          | 23 +++++++++++++++++++++++
 source3/lib/messages_ctdb.c     |  8 ++++++++
 source3/lib/messages_ctdb_ref.c | 12 ++++++++++++
 source3/lib/messages_dgm.c      | 14 ++++++++++++++
 source3/lib/messages_dgm_ref.c  | 12 ++++++++++++
 5 files changed, 69 insertions(+)

diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index 82a177856f60..1a5ea4659aa4 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -365,6 +365,11 @@ static bool messaging_alert_event_contexts(struct messaging_context *ctx)
 		 * alternatively would be to track whether the
 		 * immediate has already been scheduled. For
 		 * now, avoid that complexity here.
+		 *
+		 * reg->ev and ctx->event_ctx can't
+		 * be wrapper tevent_context pointers
+		 * so we don't need to use
+		 * tevent_context_same_loop().
 		 */
 
 		if (reg->ev == ctx->event_ctx) {
@@ -493,6 +498,12 @@ static NTSTATUS messaging_init_internal(TALLOC_CTX *mem_ctx,
 
 	sec_init();
 
+	if (tevent_context_is_wrapper(ev)) {
+		/* This is really a programmer error! */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
 	lck_path = lock_path("msg.lock");
 	if (lck_path == NULL) {
 		return NT_STATUS_NO_MEMORY;
@@ -1023,6 +1034,13 @@ struct tevent_req *messaging_filtered_read_send(
 	state->filter = filter;
 	state->private_data = private_data;
 
+	if (tevent_context_is_wrapper(ev)) {
+		/* This is really a programmer error! */
+		DBG_ERR("Wrapper tevent context doesn't use main context.\n");
+		tevent_req_error(req, EINVAL);
+		return tevent_req_post(req, ev);
+	}
+
 	/*
 	 * We have to defer the callback here, as we might be called from
 	 * within a different tevent_context than state->ev
@@ -1343,6 +1361,11 @@ static void messaging_dispatch_rec(struct messaging_context *msg_ctx,
 	bool consumed;
 	size_t i;
 
+	/*
+	 * ev and msg_ctx->event_ctx can't be wrapper tevent_context pointers
+	 * so we don't need to use tevent_context_same_loop().
+	 */
+
 	if (ev == msg_ctx->event_ctx) {
 		consumed = messaging_dispatch_classic(msg_ctx, rec);
 		if (consumed) {
diff --git a/source3/lib/messages_ctdb.c b/source3/lib/messages_ctdb.c
index d3e2e3f85893..a1aeb37af198 100644
--- a/source3/lib/messages_ctdb.c
+++ b/source3/lib/messages_ctdb.c
@@ -209,6 +209,14 @@ struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context(
 		return NULL;
 	}
 
+	if (tevent_context_is_wrapper(ev)) {
+		/*
+		 * This is really a programmer error!
+		 */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		return NULL;
+	}
+
 	fde = talloc(mem_ctx, struct messaging_ctdb_fde);
 	if (fde == NULL) {
 		return NULL;
diff --git a/source3/lib/messages_ctdb_ref.c b/source3/lib/messages_ctdb_ref.c
index 3570ed8ae4c8..47b4b758dac5 100644
--- a/source3/lib/messages_ctdb_ref.c
+++ b/source3/lib/messages_ctdb_ref.c
@@ -52,6 +52,18 @@ void *messaging_ctdb_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 {
 	struct msg_ctdb_ref *result, *tmp_refs;
 
+	if (tevent_context_is_wrapper(ev)) {
+		/*
+		 * This is really a programmer error!
+		 *
+		 * The main/raw tevent context should
+		 * have been registered first!
+		 */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		*err = EINVAL;
+		return NULL;
+	}
+
 	result = talloc(mem_ctx, struct msg_ctdb_ref);
 	if (result == NULL) {
 		*err = ENOMEM;
diff --git a/source3/lib/messages_dgm.c b/source3/lib/messages_dgm.c
index b8878b68b996..1c76615093c0 100644
--- a/source3/lib/messages_dgm.c
+++ b/source3/lib/messages_dgm.c
@@ -993,6 +993,12 @@ int messaging_dgm_init(struct tevent_context *ev,
 		return EEXIST;
 	}
 
+	if (tevent_context_is_wrapper(ev)) {
+		/* This is really a programmer error! */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		return EINVAL;
+	}
+
 	ctx = talloc_zero(NULL, struct messaging_dgm_context);
 	if (ctx == NULL) {
 		goto fail_nomem;
@@ -1673,6 +1679,14 @@ struct messaging_dgm_fde *messaging_dgm_register_tevent_context(
 		return NULL;
 	}
 
+	if (tevent_context_is_wrapper(ev)) {
+		/*
+		 * This is really a programmer error!
+		 */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		return NULL;
+	}
+
 	fde = talloc(mem_ctx, struct messaging_dgm_fde);
 	if (fde == NULL) {
 		return NULL;
diff --git a/source3/lib/messages_dgm_ref.c b/source3/lib/messages_dgm_ref.c
index 470dfbeabc74..fd1709617468 100644
--- a/source3/lib/messages_dgm_ref.c
+++ b/source3/lib/messages_dgm_ref.c
@@ -55,6 +55,18 @@ void *messaging_dgm_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 {
 	struct msg_dgm_ref *result, *tmp_refs;
 
+	if (tevent_context_is_wrapper(ev)) {
+		/*
+		 * This is really a programmer error!
+		 *
+		 * The main/raw tevent context should
+		 * have been registered first!
+		 */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		*err = EINVAL;
+		return NULL;
+	}
+
 	result = talloc(mem_ctx, struct msg_dgm_ref);
 	if (result == NULL) {
 		*err = ENOMEM;
-- 
2.17.1


From e97219df2f3a0085e110c3a6a271fb6f2877b2b4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 23 Mar 2018 14:48:46 +0100
Subject: [PATCH 33/37] s3:messages: allow
 messaging_{dgm,ctdb}_register_tevent_context() to use wrapper tevent_context

This is only allowed if the raw tevent context is already registered.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/lib/messages_ctdb.c | 38 ++++++++++++++++++++++++++++---------
 source3/lib/messages_dgm.c  | 38 ++++++++++++++++++++++++++++---------
 2 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/source3/lib/messages_ctdb.c b/source3/lib/messages_ctdb.c
index a1aeb37af198..11fe72661cc3 100644
--- a/source3/lib/messages_ctdb.c
+++ b/source3/lib/messages_ctdb.c
@@ -209,14 +209,6 @@ struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context(
 		return NULL;
 	}
 
-	if (tevent_context_is_wrapper(ev)) {
-		/*
-		 * This is really a programmer error!
-		 */
-		DBG_ERR("Should not be used with a wrapper tevent context\n");
-		return NULL;
-	}
-
 	fde = talloc(mem_ctx, struct messaging_ctdb_fde);
 	if (fde == NULL) {
 		return NULL;
@@ -234,7 +226,24 @@ struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context(
 			 */
 			continue;
 		}
-		if (fde_ev->ev == ev) {
+
+		/*
+		 * We can only have one tevent_fd
+		 * per low level tevent_context.
+		 *
+		 * This means any wrapper tevent_context
+		 * needs to share the structure with
+		 * the main tevent_context and/or
+		 * any sibling wrapper tevent_context.
+		 *
+		 * This means we need to use tevent_context_same_loop()
+		 * instead of just (fde_ev->ev == ev).
+		 *
+		 * Note: the tevent_context_is_wrapper() check below
+		 * makes sure that fde_ev->ev is always a raw
+		 * tevent context.
+		 */
+		if (tevent_context_same_loop(fde_ev->ev, ev)) {
 			break;
 		}
 	}
@@ -242,6 +251,17 @@ struct messaging_ctdb_fde *messaging_ctdb_register_tevent_context(
 	if (fde_ev == NULL) {
 		int sock = ctdbd_conn_get_fd(ctx->conn);
 
+		if (tevent_context_is_wrapper(ev)) {
+			/*
+			 * This is really a programmer error!
+			 *
+			 * The main/raw tevent context should
+			 * have been registered first!
+			 */
+			DBG_ERR("Should not be used with a wrapper tevent context\n");
+			return NULL;
+		}
+
 		fde_ev = talloc(fde, struct messaging_ctdb_fde_ev);
 		if (fde_ev == NULL) {
 			return NULL;
diff --git a/source3/lib/messages_dgm.c b/source3/lib/messages_dgm.c
index 1c76615093c0..0ad8f46e09f1 100644
--- a/source3/lib/messages_dgm.c
+++ b/source3/lib/messages_dgm.c
@@ -1679,14 +1679,6 @@ struct messaging_dgm_fde *messaging_dgm_register_tevent_context(
 		return NULL;
 	}
 
-	if (tevent_context_is_wrapper(ev)) {
-		/*
-		 * This is really a programmer error!
-		 */
-		DBG_ERR("Should not be used with a wrapper tevent context\n");
-		return NULL;
-	}
-
 	fde = talloc(mem_ctx, struct messaging_dgm_fde);
 	if (fde == NULL) {
 		return NULL;
@@ -1704,12 +1696,40 @@ struct messaging_dgm_fde *messaging_dgm_register_tevent_context(
 			 */
 			continue;
 		}
-		if (fde_ev->ev == ev) {
+
+		/*
+		 * We can only have one tevent_fd
+		 * per low level tevent_context.
+		 *
+		 * This means any wrapper tevent_context
+		 * needs to share the structure with
+		 * the main tevent_context and/or
+		 * any sibling wrapper tevent_context.
+		 *
+		 * This means we need to use tevent_context_same_loop()
+		 * instead of just (fde_ev->ev == ev).
+		 *
+		 * Note: the tevent_context_is_wrapper() check below
+		 * makes sure that fde_ev->ev is always a raw
+		 * tevent context.
+		 */
+		if (tevent_context_same_loop(fde_ev->ev, ev)) {
 			break;
 		}
 	}
 
 	if (fde_ev == NULL) {
+		if (tevent_context_is_wrapper(ev)) {
+			/*
+			 * This is really a programmer error!
+			 *
+			 * The main/raw tevent context should
+			 * have been registered first!
+			 */
+			DBG_ERR("Should not be used with a wrapper tevent context\n");
+			return NULL;
+		}
+
 		fde_ev = talloc(fde, struct messaging_dgm_fde_ev);
 		if (fde_ev == NULL) {
 			return NULL;
-- 
2.17.1


From 604621acd0f4a35b6493e4563dcfbb6a9d30490b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 23 Mar 2018 14:48:46 +0100
Subject: [PATCH 34/37] s3:messages: allow messaging_dgm_ref() to use wrapper
 tevent_context

This is only allowed if the raw tevent context is already registered.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/lib/messages_dgm_ref.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/source3/lib/messages_dgm_ref.c b/source3/lib/messages_dgm_ref.c
index fd1709617468..12ff21ca6283 100644
--- a/source3/lib/messages_dgm_ref.c
+++ b/source3/lib/messages_dgm_ref.c
@@ -55,18 +55,6 @@ void *messaging_dgm_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 {
 	struct msg_dgm_ref *result, *tmp_refs;
 
-	if (tevent_context_is_wrapper(ev)) {
-		/*
-		 * This is really a programmer error!
-		 *
-		 * The main/raw tevent context should
-		 * have been registered first!
-		 */
-		DBG_ERR("Should not be used with a wrapper tevent context\n");
-		*err = EINVAL;
-		return NULL;
-	}
-
 	result = talloc(mem_ctx, struct msg_dgm_ref);
 	if (result == NULL) {
 		*err = ENOMEM;
@@ -87,6 +75,18 @@ void *messaging_dgm_ref(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 	if (refs == NULL) {
 		int ret;
 
+		if (tevent_context_is_wrapper(ev)) {
+			/*
+			 * This is really a programmer error!
+			 *
+			 * The main/raw tevent context should
+			 * have been registered first!
+			 */
+			DBG_ERR("Should not be used with a wrapper tevent context\n");
+			*err = EINVAL;
+			return NULL;
+		}
+
 		ret = messaging_dgm_init(ev, unique, socket_dir, lockfile_dir,
 					 msg_dgm_ref_recv, NULL);
 		DBG_DEBUG("messaging_dgm_init returned %s\n", strerror(ret));
-- 
2.17.1


From c1dfd1a5516470c6d5ec12249093a1cff5953ff2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 23 Mar 2018 14:48:46 +0100
Subject: [PATCH 35/37] s3:messages: allow messaging_filtered_read_send() to
 use wrapper tevent_context

As it gets 'messaging_context' as argument, we're sure a messaging context
with a raw tevent context already exist.

It means we can allow a wrapper tevent context that wrapps the main tevent
context of the messaging context.

The use of tevent_req_defer_callback() makes sure that the callers
callback function calls messaging_filtered_read_recv() from the
correct "wrapped" environment.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/lib/messages.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index 1a5ea4659aa4..dab53f1c48e3 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -206,7 +206,7 @@ static bool messaging_register_event_context(struct messaging_context *ctx,
 			continue;
 		}
 
-		if (reg->ev == ev) {
+		if (tevent_context_same_loop(reg->ev, ev)) {
 			reg->refcount += 1;
 			return true;
 		}
@@ -255,7 +255,7 @@ static bool messaging_deregister_event_context(struct messaging_context *ctx,
 			continue;
 		}
 
-		if (reg->ev == ev) {
+		if (tevent_context_same_loop(reg->ev, ev)) {
 			reg->refcount -= 1;
 
 			if (reg->refcount == 0) {
@@ -1034,7 +1034,9 @@ struct tevent_req *messaging_filtered_read_send(
 	state->filter = filter;
 	state->private_data = private_data;
 
-	if (tevent_context_is_wrapper(ev)) {
+	if (tevent_context_is_wrapper(ev) &&
+	    !tevent_context_same_loop(ev, msg_ctx->event_ctx))
+	{
 		/* This is really a programmer error! */
 		DBG_ERR("Wrapper tevent context doesn't use main context.\n");
 		tevent_req_error(req, EINVAL);
@@ -1043,7 +1045,11 @@ struct tevent_req *messaging_filtered_read_send(
 
 	/*
 	 * We have to defer the callback here, as we might be called from
-	 * within a different tevent_context than state->ev
+	 * within a different tevent_context than state->ev.
+	 *
+	 * This is important for two cases:
+	 * 1. nested event contexts, used by blocking ctdb calls
+	 * 2. possible impersonation using wrapper tevent contexts.
 	 */
 	tevent_req_defer_callback(req, state->ev);
 
@@ -1339,7 +1345,7 @@ static bool messaging_dispatch_waiters(struct messaging_context *msg_ctx,
 
 		state = tevent_req_data(
 			req, struct messaging_filtered_read_state);
-		if ((ev == state->ev) &&
+		if (tevent_context_same_loop(ev, state->ev) &&
 		    state->filter(rec, state->private_data)) {
 			messaging_filtered_read_done(req, rec);
 			return true;
-- 
2.17.1


From 13f826e6700fee77039ef2805004db5aae9f352a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 19 May 2018 10:14:25 +0200
Subject: [PATCH 36/37] s4:messaging: allow imessaging_post_handler() to free
 the messaging context from a handler

In usecases like using messaging_client_init() with irpc processing we may
free the imessaging_context during the messaging handler.
imessaging_post_handler() is not yet really used, but it will change in
the next commits. imessaging_post_state is a child of imessaging_context
and might be implicitly free'ed before the explicit TALLOC_FREE(state).

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source4/lib/messaging/messaging.c | 33 +++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c
index b8d4e50f12c7..8903322bdc7f 100644
--- a/source4/lib/messaging/messaging.c
+++ b/source4/lib/messaging/messaging.c
@@ -433,18 +433,48 @@ fail:
 
 struct imessaging_post_state {
 	struct imessaging_context *msg_ctx;
+	struct imessaging_post_state **busy_ref;
 	size_t buf_len;
 	uint8_t buf[];
 };
 
+static int imessaging_post_state_destructor(struct imessaging_post_state *state)
+{
+	if (state->busy_ref != NULL) {
+		*state->busy_ref = NULL;
+		state->busy_ref = NULL;
+	}
+	return 0;
+}
+
 static void imessaging_post_handler(struct tevent_context *ev,
 				    struct tevent_immediate *ti,
 				    void *private_data)
 {
 	struct imessaging_post_state *state = talloc_get_type_abort(
 		private_data, struct imessaging_post_state);
+
+	/*
+	 * In usecases like using messaging_client_init() with irpc processing
+	 * we may free the imessaging_context during the messaging handler.
+	 * imessaging_post_state is a child of imessaging_context and
+	 * might be implicitly free'ed before the explicit TALLOC_FREE(state).
+	 *
+	 * The busy_ref pointer makes sure the destructor clears
+	 * the local 'state' variable.
+	 */
+
+	SMB_ASSERT(state->busy_ref == NULL);
+	state->busy_ref = &state;
+
 	imessaging_dgm_recv(ev, state->buf, state->buf_len, NULL, 0,
 			    state->msg_ctx);
+
+	if (state == NULL) {
+		return;
+	}
+
+	state->busy_ref = NULL;
 	TALLOC_FREE(state);
 }
 
@@ -461,6 +491,8 @@ static int imessaging_post_self(struct imessaging_context *msg,
 	}
 	talloc_set_name_const(state, "struct imessaging_post_state");
 
+	talloc_set_destructor(state, imessaging_post_state_destructor);
+
 	ti = tevent_create_immediate(state);
 	if (ti == NULL) {
 		TALLOC_FREE(state);
@@ -468,6 +500,7 @@ static int imessaging_post_self(struct imessaging_context *msg,
 	}
 
 	state->msg_ctx = msg;
+	state->busy_ref = NULL;
 	state->buf_len = buf_len;
 	memcpy(state->buf, buf, buf_len);
 
-- 
2.17.1


From 02c60f05c79b902eeec7597c375ad5b4f5ea63ad Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 18 May 2018 16:28:47 +0200
Subject: [PATCH 37/37] s4:messaging: make sure only imessaging_client_init()
 can be used with a wrapper tevent_context wrapper

imessaging_client_init() can be used with a wrapper tevent_context,
but only if a global messaging_dgm_ref() already exist.

All other uses of imessaging_init() and imessaging_client_init()
require a raw tevent_context.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source4/lib/messaging/messaging.c | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c
index 8903322bdc7f..935951f3fbae 100644
--- a/source4/lib/messaging/messaging.c
+++ b/source4/lib/messaging/messaging.c
@@ -319,7 +319,7 @@ NTSTATUS imessaging_reinit_all(void)
 /*
   create the listening socket and setup the dispatcher
 */
-struct imessaging_context *imessaging_init(TALLOC_CTX *mem_ctx,
+static struct imessaging_context *imessaging_init_internal(TALLOC_CTX *mem_ctx,
 					   struct loadparm_context *lp_ctx,
 					   struct server_id server_id,
 					   struct tevent_context *ev)
@@ -573,6 +573,30 @@ static void imessaging_dgm_recv(struct tevent_context *ev,
 	}
 }
 
+struct imessaging_context *imessaging_init(TALLOC_CTX *mem_ctx,
+					   struct loadparm_context *lp_ctx,
+					   struct server_id server_id,
+					   struct tevent_context *ev)
+{
+	if (ev == NULL) {
+		return NULL;
+	}
+
+	if (tevent_context_is_wrapper(ev)) {
+		/*
+		 * This is really a programmer error!
+		 *
+		 * The main/raw tevent context should
+		 * have been registered first!
+		 */
+		DBG_ERR("Should not be used with a wrapper tevent context\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	return imessaging_init_internal(mem_ctx, lp_ctx, server_id, ev);
+}
+
 /*
    A hack, for the short term until we get 'client only' messaging in place
 */
@@ -589,7 +613,7 @@ struct imessaging_context *imessaging_client_init(TALLOC_CTX *mem_ctx,
 	/* This is because we are not in the s3 serverid database */
 	id.unique_id = SERVERID_UNIQUE_ID_NOT_TO_VERIFY;
 
-	return imessaging_init(mem_ctx, lp_ctx, id, ev);
+	return imessaging_init_internal(mem_ctx, lp_ctx, id, ev);
 }
 /*
   a list of registered irpc server functions
-- 
2.17.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20180711/d6bc4d18/signature-0001.sig>


More information about the samba-technical mailing list