[PATCH] tevent glib event loop glue

Ralph Boehme slow at samba.org
Sat Feb 13 07:19:23 UTC 2016


Hi!

Attached is a patchset that adds support for polling a glib
g_main_context from tevent. Later commits make use of it in the mdsvc
RPC server.

The second patch adds a test binary that can be used to run query
Gnome Tracker either using the native glib event loop or tevent.

I'm still unsure whether we want this added directly to tevent at this
early stage, or instead better put it to source3/lib/. It makes no
difference for Samba internal consumers.

I would like to see this landing in 4.4, as it's such a nice cleanup
in the mdssvc RPC server (aka Spotlight).

Please review & comment. Thanks!

Cheerio!
-slow
-------------- next part --------------
From 60f586ba6c98ca5f285fe5dc6718677329044028 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 20 Jan 2016 15:08:31 +0100
Subject: [PATCH 1/5] tevent: glib g_main_context glue

Add a tevent function tevent_glib_glue_create() that takes glib
GMainContext and adds its event sources to a tevent context. tevent will
poll the sources and run handlers for pending events as detailed in the
glib documentation:

https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html

If tevent was built without glib support, the function will always
return NULL with an error number ENOSYS.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/tevent.h      |  39 +++
 lib/tevent/tevent_glib.c | 709 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/tevent/wscript       |  13 +-
 3 files changed, 760 insertions(+), 1 deletion(-)
 create mode 100644 lib/tevent/tevent_glib.c

diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h
index cb95507..6d1f6a1 100644
--- a/lib/tevent/tevent.h
+++ b/lib/tevent/tevent.h
@@ -1750,6 +1750,45 @@ void tevent_thread_proxy_schedule(struct tevent_thread_proxy *tp,
 				  tevent_immediate_handler_t handler,
 				  void *pp_private_data);
 
+typedef struct _GMainContext GMainContext;
+
+/**
+ * @brief Add a glib GmainContext to a tevent context
+ *
+ * tevent will poll the glib event sources and run handlers for
+ * pending events as detailed in the glib documentation:
+ *
+ * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
+ *
+ * If tevent was built without glib support, this function will always return
+ * NULL with an error number ENOSYS.
+ *
+ * @param[in]  mem_ctx          Memory context to use
+ *
+ * @param[in]  ev               Event context to use
+ *
+ * @param[in]  gmain_ctx        GMainContext that will be added to tevent
+ *
+ * @return                      A handle on the glue context that binds the
+ *                              the GMainContext to tevent. Pass the glue handle to
+ *                              tevent_glib_glue_quit() in a callback when you want
+ *                              stop processing glib events.
+ *                              You must not call talloc_free() on the handle while
+ *                              the loop is still in use and attached to tevent.
+ */
+struct tevent_glib_glue *tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 GMainContext *gmain_ctx);
+
+/**
+ * @brief Stop polling a GMainContext
+ *
+ * Used in a callback when you want to stop processing glib events.
+ *
+ * @param[in]  glue             And tevent_glib_glue handle
+ */
+void tevent_glib_glue_quit(struct tevent_glib_glue *glue);
+
 #ifdef TEVENT_DEPRECATED
 #ifndef _DEPRECATED_
 #if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 )
diff --git a/lib/tevent/tevent_glib.c b/lib/tevent/tevent_glib.c
new file mode 100644
index 0000000..e343e67
--- /dev/null
+++ b/lib/tevent/tevent_glib.c
@@ -0,0 +1,709 @@
+/*
+   Unix SMB/CIFS implementation.
+   Integration of a glib g_main_context into a tevent_context
+   Copyright (C) Stefan Metzmacher 2016
+   Copyright (C) Ralph Boehme 2016
+
+     ** 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 "system/filesys.h"
+#include "system/select.h"
+#include "tevent.h"
+#include "tevent_internal.h"
+
+#ifdef HAVE_GLIB
+
+#include <glib.h>
+
+struct tevent_fd_map {
+	int fd;
+	struct tevent_fd *fd_event;
+};
+
+struct tevent_glib_glue {
+	struct tevent_context *ev;
+	GMainContext *gmain_ctx;
+	bool quit;
+
+	struct tevent_timer *retry_timer;
+	gint gtimeout;
+	gint gpriority;
+	GPollFD *gpollfds;
+	gint num_gpollfds;
+	GPollFD *prev_gpollfds;
+	gint num_prev_gpollfds;
+
+	struct tevent_fd_map *fd_map;
+	size_t num_maps;
+	struct tevent_timer *timer;
+	struct tevent_immediate *im;
+	bool scheduled_im;
+	struct pollfd *pollfds;
+};
+
+static bool tevent_glib_prepare(struct tevent_glib_glue *glue);
+static bool tevent_glib_process(struct tevent_glib_glue *glue);
+static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue);
+static void tevent_glib_fd_handler(struct tevent_context *ev,
+				   struct tevent_fd *fde,
+				   uint16_t flags,
+				   void *private_data);
+
+typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2);
+typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue,
+			      const GPollFD *new, const GPollFD *old);
+typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue, const GPollFD *fd);
+typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue, const GPollFD *fd);
+
+/**
+ * Compare two GPollFD arrays
+ *
+ * For every element that exists in gfds and prev_gfds found_fn() is called.
+ * For every element in gfds but not in prev_gfds, new_fn() is called.
+ * For every element in prev_gfds but not in gfds removed_fn() is called.
+ **/
+static bool cmp_gfds(struct tevent_glib_glue *glue,
+		     GPollFD *gfds, GPollFD *prev_gfds,
+		     size_t num_gfds, size_t num_prev_gfds,
+		     gfds_cmp_cb cmp_cb,
+		     gfds_found_cb found_cb,
+		     gfds_new_cb new_cb,
+		     gfds_removed_cb removed_cb)
+{
+	bool ok;
+	size_t i = 0, j = 0;
+	int cmp;
+
+	while (i < num_gfds && j < num_prev_gfds) {
+		cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
+		if (cmp == 0) {
+			ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
+			if (!ok) {
+				return false;
+			}
+			i++;
+			j++;
+		} else if (cmp < 0) {
+			ok = new_cb(glue, &gfds[i]);
+			if (!ok) {
+				return false;
+			}
+			i++;
+		} else {
+			ok = removed_cb(glue, &prev_gfds[j]);
+			if (!ok) {
+				return false;
+			}
+			j++;
+		}
+	}
+
+	while (i < num_gfds) {
+		ok = new_cb(glue, &gfds[i++]);
+		if (!ok) {
+			return false;
+		}
+	}
+
+	while (j < num_prev_gfds) {
+		ok = removed_cb(glue, &prev_gfds[j++]);
+		if (!ok) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int glib_fd_cmp_func(const void *p1, const void *p2)
+{
+	const GPollFD *lhs = p1;
+	const GPollFD *rhs = p2;
+
+	if (lhs->fd < rhs->fd) {
+		return -1;
+	} else if (lhs->fd > rhs->fd) {
+		return 1;
+	}
+
+	return 0;
+}
+
+static bool match_gfd_cb(struct tevent_glib_glue *glue,
+			 const GPollFD *new_gfd,
+			 const GPollFD *old_gfd)
+{
+	size_t i;
+	struct tevent_fd *fd_event = NULL;
+
+	if (new_gfd->events == old_gfd->events) {
+		return true;
+	}
+
+	for (i = 0; i < glue->num_maps; i++) {
+		if (glue->fd_map[i].fd == new_gfd->fd) {
+			break;
+		}
+	}
+
+ 	if (i == glue->num_maps) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
+		return false;
+	}
+
+	fd_event = glue->fd_map[i].fd_event;
+	if (fd_event == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "fd_event for fd %d is NULL\n", new_gfd->fd);
+		return false;
+	}
+
+	tevent_fd_set_flags(fd_event, 0);
+
+	if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
+		TEVENT_FD_READABLE(fd_event);
+	}
+	if (new_gfd->events & G_IO_OUT) {
+		TEVENT_FD_WRITEABLE(fd_event);
+	}
+
+	return true;
+}
+
+static int fd_map_destructor(struct tevent_fd_map *map)
+{
+	size_t n = talloc_array_length(map);
+	size_t i;
+
+	for (i = 0; i < n; i++) {
+		TALLOC_FREE(map[i].fd_event);
+	}
+
+	return 0;
+}
+
+static bool add_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
+{
+	struct tevent_fd *fd_event = NULL;
+	uint16_t events;
+
+	events = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) ?
+		TEVENT_FD_READ : 0;
+	events |= (gfd->events & G_IO_OUT) ? TEVENT_FD_WRITE : 0;
+
+	fd_event = tevent_add_fd(glue->ev, glue->fd_map,
+				 gfd->fd,
+				 events,
+				 tevent_glib_fd_handler,
+				 glue);
+	if (fd_event == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "tevent_add_fd failed\n");
+		return false;
+	}
+
+	glue->fd_map = talloc_realloc(glue, glue->fd_map,
+				      struct tevent_fd_map,
+				      glue->num_maps + 1);
+	if (glue->fd_map == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "talloc_realloc failed\n");
+		return false;
+	}
+	talloc_set_destructor(glue->fd_map, fd_map_destructor);
+
+	glue->fd_map[glue->num_maps].fd = gfd->fd;
+	glue->fd_map[glue->num_maps].fd_event = fd_event;
+	glue->num_maps++;
+
+	tevent_debug(glue->ev, TEVENT_DEBUG_TRACE,
+		     "added tevent_fd for glib fd %d\n", gfd->fd);
+
+	return true;
+}
+
+static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
+{
+	size_t i;
+
+	for (i = 0; i < glue->num_maps; i++) {
+		if (glue->fd_map[i].fd == gfd->fd) {
+			break;
+		}
+	}
+
+ 	if (i == glue->num_maps) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
+		return false;
+	}
+
+	TALLOC_FREE(glue->fd_map[i].fd_event);
+
+	if (i + 1 < glue->num_maps) {
+		memmove(&glue->fd_map[i], &glue->fd_map[i+1],
+			(glue->num_maps - (i + 1)) * sizeof(struct tevent_fd_map));
+	}
+
+	glue->num_maps--;
+
+	glue->fd_map = talloc_realloc(glue, glue->fd_map,
+				      struct tevent_fd_map,
+				      glue->num_maps);
+	if (glue->fd_map == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "talloc_realloc failed\n");
+		return false;
+	}
+	talloc_set_destructor(glue->fd_map, fd_map_destructor);
+
+	return true;
+}
+
+static void tevent_glib_fd_handler(struct tevent_context *ev,
+				   struct tevent_fd *fde,
+				   uint16_t flags,
+				   void *private_data)
+{
+	struct tevent_glib_glue *glue = talloc_get_type_abort(
+		private_data, struct tevent_glib_glue);
+
+	tevent_glib_process(glue);
+
+	return;
+}
+
+static void tevent_glib_timer_handler(struct tevent_context *ev,
+				      struct tevent_timer *te,
+				      struct timeval current_time,
+				      void *private_data)
+{
+	struct tevent_glib_glue *glue = talloc_get_type_abort(
+		private_data, struct tevent_glib_glue);
+
+	glue->timer = NULL;
+	tevent_glib_process(glue);
+
+	return;
+}
+
+static void tevent_glib_im_handler(struct tevent_context *ev,
+				   struct tevent_immediate *im,
+				   void *private_data)
+{
+	struct tevent_glib_glue *glue = talloc_get_type_abort(
+		private_data, struct tevent_glib_glue);
+
+	glue->scheduled_im = false;
+	tevent_glib_process(glue);
+
+	return;
+}
+
+static bool save_current_fdset(struct tevent_glib_glue *glue)
+{
+	/* Save old glib fds, we only grow the prev array */
+	if (glue->num_prev_gpollfds < glue->num_gpollfds) {
+		glue->prev_gpollfds = talloc_realloc(glue,
+						     glue->prev_gpollfds,
+						     GPollFD,
+						     glue->num_gpollfds);
+		if (glue->prev_gpollfds == NULL) {
+			tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+				     "talloc_realloc failed\n");
+			return false;
+		}
+	}
+	glue->num_prev_gpollfds = glue->num_gpollfds;
+	if (glue->num_gpollfds > 0) {
+		memcpy(glue->prev_gpollfds, glue->gpollfds,
+		       sizeof(GPollFD) * glue->num_gpollfds);
+		memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
+	}
+
+	return true;
+}
+
+static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
+{
+	bool ok;
+	gint num_fds;
+
+	ok = save_current_fdset(glue);
+	if (!ok) {
+		return false;
+	}
+
+	while (true) {
+		num_fds = g_main_context_query(glue->gmain_ctx,
+					       glue->gpriority,
+					       &glue->gtimeout,
+					       glue->gpollfds,
+					       glue->num_gpollfds);
+		if (num_fds == glue->num_gpollfds) {
+			break;
+		}
+		glue->gpollfds = talloc_realloc(glue,
+						glue->gpollfds,
+						GPollFD,
+						num_fds);
+		if (num_fds > 0 && glue->gpollfds == NULL) {
+			tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+				     "talloc_realloc failed\n");
+			return false;
+		}
+		glue->num_gpollfds = num_fds;
+	};
+
+	if (glue->num_gpollfds > 0) {
+		qsort(glue->gpollfds, num_fds, sizeof(GPollFD), glib_fd_cmp_func);
+	}
+
+	tevent_debug(glue->ev, TEVENT_DEBUG_TRACE,
+		     "get_glib_fds_and_timeout: num fds: %d, timeout: %d ms\n",
+		     num_fds, glue->gtimeout);
+
+	return true;
+}
+
+static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
+{
+	bool ok;
+
+	ok = cmp_gfds(glue,
+		      glue->gpollfds,
+		      glue->prev_gpollfds,
+		      glue->num_gpollfds,
+		      glue->num_prev_gpollfds,
+		      glib_fd_cmp_func,
+		      match_gfd_cb,
+		      add_gfd_cb,
+		      remove_gfd_cb);
+	if (!ok) {
+		return false;
+	}
+
+	TALLOC_FREE(glue->timer);
+	if ((glue->gtimeout == 0) && (!glue->scheduled_im)) {
+		/*
+		 * Schedule an immediate event. We use a immediate event and not
+		 * an immediate timer event, because the former can be reused.
+		 *
+		 * We may be called in a loop in tevent_glib_process() and only
+		 * want to schedule this once, so we remember the fact.
+		 *
+		 * Doing this here means we occasionally schedule an unneeded
+		 * immediate event, but it avoids leaking abstraction into upper
+		 * layers.
+		 */
+		tevent_schedule_immediate(glue->im, glue->ev,
+					  tevent_glib_im_handler,
+					  glue);
+		glue->scheduled_im = true;
+	} else if (glue->gtimeout > 0) {
+		uint64_t microsec = glue->gtimeout * 1000;
+		struct timeval tv = tevent_timeval_current_ofs(microsec / 1000000,
+							       microsec % 1000000);
+
+		glue->timer = tevent_add_timer(glue->ev, glue,
+					       tv,
+					       tevent_glib_timer_handler,
+					       glue);
+		if (glue->timer == NULL) {
+			tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+				     "tevent_add_timer failed\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static void tevent_glib_retry_timer(struct tevent_context *ev,
+				    struct tevent_timer *te,
+				    struct timeval current_time,
+				    void *private_data)
+{
+	struct tevent_glib_glue *glue = talloc_get_type_abort(
+		private_data, struct tevent_glib_glue);
+
+	glue->retry_timer = NULL;
+	(void)tevent_glib_prepare(glue);
+}
+
+static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
+{
+	bool ok;
+	gboolean gok, source_ready;
+
+	gok = g_main_context_acquire(glue->gmain_ctx);
+	if (!gok) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_TRACE,
+			     "couldn't acquire g_main_context\n");
+
+		tevent_glib_glue_cleanup(glue);
+
+		glue->retry_timer = tevent_add_timer(
+			glue->ev, glue,
+			tevent_timeval_current_ofs(0, 1000),
+			tevent_glib_retry_timer,
+			glue);
+		if (glue->retry_timer == NULL) {
+			tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+				     __location__);
+			return false;
+		}
+		return true;
+	}
+
+	source_ready = g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
+	if (source_ready) {
+		g_main_context_dispatch(glue->gmain_ctx);
+	}
+
+	ok = get_glib_fds_and_timeout(glue);
+	if (!ok) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     __location__);
+		tevent_glib_glue_quit(glue);
+		return false;
+	}
+
+	tevent_glib_update_events(glue);
+
+	return true;
+}
+
+static short gpoll_to_poll_event(gushort gevent)
+{
+	short pevent = 0;
+
+	if (gevent & G_IO_IN) {
+		pevent |= POLLIN;
+	}
+	if (gevent & G_IO_OUT) {
+		pevent |= POLLOUT;
+	}
+	if (gevent & G_IO_HUP) {
+		pevent |= POLLHUP;
+	}
+	if (gevent & G_IO_ERR) {
+		pevent |= POLLERR;
+	}
+
+	return pevent;
+}
+
+static gushort poll_to_gpoll_event(short pevent)
+{
+	gushort gevent = 0;
+
+	if (pevent & POLLIN) {
+		gevent |= G_IO_IN;
+	}
+	if (pevent & POLLOUT) {
+		gevent |= G_IO_OUT;
+	}
+	if (pevent & POLLHUP) {
+		gevent |= G_IO_HUP;
+	}
+	if (pevent & POLLERR) {
+		gevent |= G_IO_ERR;
+	}
+
+	return gevent;
+}
+
+static bool gpoll_to_poll_fds(struct tevent_glib_glue *glue)
+{
+	size_t i;
+
+	TALLOC_FREE(glue->pollfds);
+
+	glue->pollfds = talloc_zero_array(glue, struct pollfd,
+					  glue->num_gpollfds);
+	if (glue->pollfds == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "talloc_zero_array failed\n");
+		return false;
+	}
+
+	for (i = 0; i < glue->num_gpollfds; i++) {
+		glue->pollfds[i].fd = glue->gpollfds[i].fd;
+		glue->pollfds[i].events = gpoll_to_poll_event(
+			glue->gpollfds[i].events);
+	}
+
+	return true;
+}
+
+static void poll_to_gpoll_revents(struct tevent_glib_glue *glue)
+{
+	size_t i;
+
+	for (i = 0; i < glue->num_gpollfds; i++) {
+		glue->gpollfds[i].revents = poll_to_gpoll_event(
+			glue->pollfds[i].revents);
+	}
+}
+
+static bool tevent_glib_process(struct tevent_glib_glue *glue)
+{
+	bool ok;
+	int num_ready;
+
+	ok = gpoll_to_poll_fds(glue);
+	if (!ok) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "gpoll_to_poll_fds failed\n");
+		tevent_glib_glue_quit(glue);
+		return false;
+	}
+
+	num_ready = poll(glue->pollfds, glue->num_gpollfds, 0);
+	if (num_ready == -1) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "poll: %s\n", strerror(errno));
+	}
+
+	if (num_ready > 0) {
+		poll_to_gpoll_revents(glue);
+	}
+
+	tevent_debug(glue->ev, TEVENT_DEBUG_TRACE,
+		     "tevent_glib_process: num_ready: %d\n", num_ready);
+
+	do {
+		bool sources_ready;
+
+		sources_ready = g_main_context_check(glue->gmain_ctx,
+						     glue->gpriority,
+						     glue->gpollfds,
+						     glue->num_gpollfds);
+		if (!sources_ready) {
+			break;
+		}
+
+		g_main_context_dispatch(glue->gmain_ctx);
+
+		if (glue->quit) {
+			/* Set via tevent_glib_glue_quit() */
+			g_main_context_release(glue->gmain_ctx);
+			return true;
+		}
+
+		/*
+		 * This is an optimisation for the following case:
+		 *
+		 * If g_main_context_query() returns a timeout value of 0 this
+		 * implicates that there may be more glib event sources ready.
+		 * This avoids sheduling an immediate event and going through
+		 * tevent_loop_once().
+		 */
+		if (glue->gtimeout != 0) {
+			break;
+		}
+
+		/*
+		 * Give other glib threads a chance to grab the context,
+		 * tevent_glib_prepare() will then re-acquire it
+		 */
+		g_main_context_release(glue->gmain_ctx);
+
+		ok = tevent_glib_prepare(glue);
+		if (!ok) {
+			tevent_glib_glue_quit(glue);
+			return false;
+		}
+	} while (true);
+
+	/*
+	 * Give other glib threads a chance to grab the context,
+	 * tevent_glib_prepare() will then re-acquire it
+	 */
+	g_main_context_release(glue->gmain_ctx);
+
+	ok = tevent_glib_prepare(glue);
+	if (!ok) {
+		tevent_glib_glue_quit(glue);
+		return false;
+	}
+
+	return true;
+}
+
+static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
+{
+	TALLOC_FREE(glue->fd_map);
+	TALLOC_FREE(glue->gpollfds);
+	TALLOC_FREE(glue->prev_gpollfds);
+	TALLOC_FREE(glue->timer);
+	TALLOC_FREE(glue->retry_timer);
+	TALLOC_FREE(glue->im);
+	glue->num_gpollfds = 0;
+	glue->num_prev_gpollfds = 0;
+}
+
+void tevent_glib_glue_quit(struct tevent_glib_glue *glue)
+{
+	tevent_glib_glue_cleanup(glue);
+	glue->quit = true;
+	return;
+}
+
+struct tevent_glib_glue *tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 GMainContext *gmain_ctx)
+{
+	bool ok;
+	struct tevent_glib_glue *glue = NULL;
+
+	glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
+	if (glue == NULL) {
+		tevent_debug(glue->ev, TEVENT_DEBUG_FATAL,
+			     "talloc_zero failed\n");
+		return NULL;
+	}
+
+	*glue = (struct tevent_glib_glue) {
+		.ev = ev,
+		.gmain_ctx = gmain_ctx,
+	};
+
+	glue->im = tevent_create_immediate(glue);
+
+	ok = tevent_glib_prepare(glue);
+	if (!ok) {
+		TALLOC_FREE(glue);
+		return NULL;
+	}
+
+	return glue;
+}
+#else
+struct tevent_glib_glue *tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 GMainContext *gmain_ctx)
+{
+	errno = ENOSYS;
+	return NULL;
+}
+#endif
diff --git a/lib/tevent/wscript b/lib/tevent/wscript
index 103cc06..c75e9cf 100755
--- a/lib/tevent/wscript
+++ b/lib/tevent/wscript
@@ -73,6 +73,11 @@ def configure(conf):
             Logs.warn('Disabling pytevent as python devel libs not found')
             conf.env.disable_python = True
 
+    if conf.CHECK_CFG(package='glib-2.0', args='--cflags --libs',
+                      msg='Checking for glib-2.0', uselib_store="GLIB-2.0"):
+        if (conf.CHECK_HEADERS('glib.h', lib='glib-2.0') and conf.CHECK_LIB('glib-2.0', shlib=True)):
+            conf.DEFINE('HAVE_GLIB', 1)
+
     conf.SAMBA_CONFIG_H()
 
     conf.SAMBA_CHECK_UNDEFINED_SYMBOL_FLAGS()
@@ -86,12 +91,18 @@ def build(bld):
              tevent_poll.c tevent_threads.c
              tevent_signal.c tevent_standard.c tevent_timed.c tevent_util.c tevent_wakeup.c'''
 
+    tevent_deps = 'replace talloc'
+
     if bld.CONFIG_SET('HAVE_EPOLL'):
         SRC += ' tevent_epoll.c'
 
     if bld.CONFIG_SET('HAVE_SOLARIS_PORTS'):
         SRC += ' tevent_port.c'
 
+    if bld.CONFIG_SET('HAVE_GLIB'):
+        SRC += ' tevent_glib.c'
+        tevent_deps += ' glib-2.0'
+
     if bld.env.standalone_tevent:
         bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
         private_library = False
@@ -101,7 +112,7 @@ def build(bld):
     if not bld.CONFIG_SET('USING_SYSTEM_TEVENT'):
         bld.SAMBA_LIBRARY('tevent',
                           SRC,
-                          deps='replace talloc',
+                          deps=tevent_deps,
                           enabled= not bld.CONFIG_SET('USING_SYSTEM_TEVENT'),
                           includes='.',
                           abi_directory='ABI',
-- 
2.5.0


From 9fa152c1360d0cf9c1dd2f7b77cbbfd054593ad2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 22 Jan 2016 15:38:39 +0100
Subject: [PATCH 2/5] s3: tevent-glib test utiltity

A small utilitly useful for tesing the tevent_glib_glue code. It runs a
tracker-sparql search query against your local tracker store that must
be setup and running.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/utils/async-tracker.c | 275 ++++++++++++++++++++++++++++++++++++++++++
 source3/wscript_build         |  11 ++
 2 files changed, 286 insertions(+)
 create mode 100644 source3/utils/async-tracker.c

diff --git a/source3/utils/async-tracker.c b/source3/utils/async-tracker.c
new file mode 100644
index 0000000..7905252
--- /dev/null
+++ b/source3/utils/async-tracker.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan.frade at nokia.com>
+ * Copyright (C) 2015, Noel Power <nopower at suse.com>
+ * Copyright (C) 2016, Ralph Boehme <slow at samba.org.>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "popt_common.h"
+#include "param.h"
+#include <tevent.h>
+/*
+ * glib uses TRUE and FALSE which was redefined by "includes.h" to be
+ * unusable, undefine so glib can establish its own working
+ * replacement.
+ */
+#undef TRUE
+#undef FALSE
+#include <libtracker-sparql/tracker-sparql.h>
+
+enum loop_type {TEVENT_LOOP, GLIB_LOOP};
+
+struct test_state {
+	enum loop_type loop_type;
+	TrackerSparqlConnection *connection;
+	GCancellable *cancellable;
+	GTimer *timer;
+	GMainLoop *loop;
+	struct tevent_context *ev;
+	struct tevent_glib_glue *glue;
+};
+
+static void cleanup(struct test_state *state)
+{
+	g_cancellable_cancel(state->cancellable);
+	g_object_unref(state->cancellable);
+	g_timer_destroy(state->timer);
+	if (state->connection) {
+		g_object_unref(state->connection);
+		state->connection = NULL;
+	}
+	if (state->loop_type == GLIB_LOOP) {
+		g_main_loop_quit(state->loop);
+	} else {
+		tevent_glib_glue_quit(state->glue);
+	}
+}
+
+static void cursor_cb(GObject      *object,
+		      GAsyncResult *res,
+		      gpointer      user_data)
+{
+	TrackerSparqlCursor *cursor;
+	GError *error = NULL;
+	gboolean more_results;
+	struct test_state *state = talloc_get_type_abort(user_data, struct test_state);
+
+	cursor = TRACKER_SPARQL_CURSOR(object);
+	more_results = tracker_sparql_cursor_next_finish(cursor,
+							 res,
+							 &error);
+
+	if (!error) {
+		static gint i = 0;
+
+		if (more_results) {
+			if (i++ < 5) {
+				int num_cols = tracker_sparql_cursor_get_n_columns(cursor);
+				int col;
+				if (i == 1) {
+					g_print("Printing first 5 results:\n");
+				}
+				for (col = 0; col < num_cols; col++) {
+					g_print(" %s ", tracker_sparql_cursor_get_string(cursor, col, NULL));
+					if (col == num_cols -1 ) {
+						g_print("\n");
+					}
+				}
+
+				if (i == 5) {
+					g_print("  ...\n");
+					g_print("  Printing nothing for remaining results\n");
+				}
+			}
+
+			tracker_sparql_cursor_next_async(cursor,
+							 state->cancellable,
+							 cursor_cb,
+							 state);
+		} else {
+			g_print("\n");
+			g_print("Async cursor next took: %.6f (for all %d results)\n",
+			         g_timer_elapsed (state->timer, NULL), i);
+
+			g_object_unref(cursor);
+			cleanup(state);
+		}
+	} else {
+		g_critical("Could not run cursor next: %s", error->message);
+
+		if (cursor) {
+			g_object_unref(cursor);
+		}
+
+		g_error_free(error);
+		cleanup(state);
+	}
+}
+
+static void query_cb(GObject      *object,
+		     GAsyncResult *res,
+		     gpointer      user_data)
+{
+	TrackerSparqlCursor *cursor;
+	GError *error = NULL;
+	struct test_state *state = talloc_get_type_abort(user_data, struct test_state);
+
+	cursor = tracker_sparql_connection_query_finish(TRACKER_SPARQL_CONNECTION (object),
+	                                                res,
+	                                                &error);
+	g_print("Async query took: %.6f\n", g_timer_elapsed(state->timer, NULL));
+
+	g_timer_start(state->timer);
+
+	if (!error) {
+		tracker_sparql_cursor_next_async(cursor,
+						 state->cancellable,
+						 cursor_cb,
+						 state);
+	} else {
+		g_critical("Could not run query: %s", error->message);
+
+		if (cursor) {
+			g_object_unref(cursor);
+		}
+
+		g_error_free(error);
+		cleanup(state);
+	}
+}
+
+static void connection_cb(GObject      *object,
+			  GAsyncResult *res,
+			  gpointer      user_data)
+{
+	struct test_state *state = talloc_get_type_abort(user_data, struct test_state);
+	GError *error = NULL;
+
+	state->connection = tracker_sparql_connection_get_finish(res, &error);
+	g_print("Async connection took: %.6f\n", g_timer_elapsed(state->timer, NULL));
+
+	g_timer_start(state->timer);
+
+	if (!error) {
+		tracker_sparql_connection_query_async(state->connection,
+						      "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) WHERE { {?s nie:url ?name}}",
+						      state->cancellable,
+						      query_cb,
+						      state);
+	} else {
+		g_critical("Could not connect: %s", error->message);
+		g_error_free(error);
+		cleanup(state);
+	}
+}
+
+static void debug_fn(void *private_data,
+		     enum tevent_debug_level level,
+		     const char *fmt,
+		     va_list ap)
+{
+	dbgtext_va(fmt, ap);
+}
+
+int main(int argc, const char **argv)
+{
+	TALLOC_CTX *mem_ctx = NULL;
+	struct test_state *state = NULL;
+	int c;
+	poptContext pc;
+	struct poptOption long_options[] = {
+		POPT_AUTOHELP
+		{"tevent",	't', POPT_ARG_NONE,	NULL, 't', "Use tevent loop" },
+		{"glib",	'g', POPT_ARG_NONE, 	NULL, 'g', "Use glib loop" },
+		POPT_COMMON_SAMBA
+		POPT_TABLEEND
+	};
+
+	mem_ctx = talloc_new(NULL);
+	if (mem_ctx == NULL) {
+		exit(1);
+	}
+
+	state = talloc_zero(mem_ctx, struct test_state);
+	if (state == NULL) {
+		exit(1);
+	}
+
+	state->loop_type = GLIB_LOOP;
+
+	setup_logging(argv[0], DEBUG_STDERR);
+	smb_init_locale();
+
+	if (!lp_load_client(get_dyn_CONFIGFILE())) {
+		fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", get_dyn_CONFIGFILE());
+		exit(1);
+	}
+
+	pc = poptGetContext(NULL, argc, argv, long_options,
+			    POPT_CONTEXT_KEEP_FIRST);
+
+	while ((c = poptGetNextOpt(pc)) != -1) {
+		switch (c) {
+		case 'g':
+			state->loop_type = GLIB_LOOP;
+			break;
+		case 't':
+			state->loop_type = TEVENT_LOOP;
+			break;
+		}
+	}
+
+	if (state->loop_type == GLIB_LOOP) {
+		state->loop = g_main_loop_new(NULL, false);
+	} else {
+		state->ev = tevent_context_init(mem_ctx);
+		if (CHECK_DEBUGLVL(10)) {
+			tevent_set_debug(state->ev, debug_fn, NULL);
+		}
+		state->glue = tevent_glib_glue_create(mem_ctx, state->ev, g_main_context_default());
+		if (state->glue == NULL) {
+			printf("tevent_glib_glue_create failed\n");
+			exit(1);
+		}
+	}
+
+	state->timer = g_timer_new();
+	state->cancellable = g_cancellable_new();
+	tracker_sparql_connection_get_async(state->cancellable,
+	                                    connection_cb,
+	                                    state);
+
+	if (state->loop_type == GLIB_LOOP) {
+		printf("entering g_main_loop_run\n");
+		g_main_loop_run(state->loop);
+	} else {
+		printf("entering tevent_loop_wait\n");
+		tevent_loop_wait(state->ev);
+
+		DBG_DEBUG("freeing glue\n");
+		TALLOC_FREE(state->glue);
+		DBG_DEBUG("freeing event context\n");
+		TALLOC_FREE(state->ev);
+	}
+
+	TALLOC_FREE(mem_ctx);
+	poptFreeContext(pc);
+
+	return 0;
+}
diff --git a/source3/wscript_build b/source3/wscript_build
index 0c7dfc2..5f2b763 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1376,6 +1376,17 @@ bld.SAMBA3_BINARY('eventlogadm',
                  param
                  LIBEVENTLOG''')
 
+bld.SAMBA3_BINARY('tevent_glib',
+                 source='utils/async-tracker.c',
+                 deps='''
+                 talloc
+                 libsmb
+                 popt_samba3
+                 param
+                 tevent ''' + bld.env['libtracker'],
+                 enabled=bld.CONFIG_SET('HAVE_GLIB'),
+                 install=False)
+
 bld.SAMBA3_BINARY('sharesec',
                  source='utils/sharesec.c lib/util_sd.c',
                  deps='''
-- 
2.5.0


From 61bd1ddb2a70174981a73458748633d978e2b7d6 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 27 Jan 2016 13:17:04 +0100
Subject: [PATCH 3/5] s3-mdssvc: add tevent context arg to mds_init_ctx

This is needed later when adding tevent_glib_glue support.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/rpc_server/mdssvc/mdssvc.c        | 1 +
 source3/rpc_server/mdssvc/mdssvc.h        | 1 +
 source3/rpc_server/mdssvc/srv_mdssvc_nt.c | 6 +++++-
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/source3/rpc_server/mdssvc/mdssvc.c b/source3/rpc_server/mdssvc/mdssvc.c
index abfea43..9d1f206 100644
--- a/source3/rpc_server/mdssvc/mdssvc.c
+++ b/source3/rpc_server/mdssvc/mdssvc.c
@@ -1809,6 +1809,7 @@ static gboolean gmainloop_timer(gpointer user_data)
  * Initialise a context per share handle
  **/
 struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
+			     struct tevent_context *ev,
 			     const struct auth_session_info *session_info,
 			     const char *path)
 {
diff --git a/source3/rpc_server/mdssvc/mdssvc.h b/source3/rpc_server/mdssvc/mdssvc.h
index 2c9dc83..3bbcdd6 100644
--- a/source3/rpc_server/mdssvc/mdssvc.h
+++ b/source3/rpc_server/mdssvc/mdssvc.h
@@ -114,6 +114,7 @@ struct mds_ctx {
 extern bool mds_init(struct messaging_context *msg_ctx);
 extern bool mds_shutdown(void);
 extern struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
+				    struct tevent_context *ev,
 				    const struct auth_session_info *session_info,
 				    const char *path);
 extern int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx);
diff --git a/source3/rpc_server/mdssvc/srv_mdssvc_nt.c b/source3/rpc_server/mdssvc/srv_mdssvc_nt.c
index cb0d759..d752140 100644
--- a/source3/rpc_server/mdssvc/srv_mdssvc_nt.c
+++ b/source3/rpc_server/mdssvc/srv_mdssvc_nt.c
@@ -18,6 +18,7 @@
  */
 
 #include "includes.h"
+#include "messages.h"
 #include "ntdomain.h"
 #include "rpc_server/mdssvc/srv_mdssvc_nt.h"
 #include "../librpc/gen_ndr/srv_mdssvc.h"
@@ -47,7 +48,10 @@ static NTSTATUS create_mdssvc_policy_handle(TALLOC_CTX *mem_ctx,
 
 	ZERO_STRUCTP(handle);
 
-	mds_ctx = mds_init_ctx(mem_ctx, p->session_info, path);
+	mds_ctx = mds_init_ctx(mem_ctx,
+			       messaging_tevent_context(p->msg_ctx),
+			       p->session_info,
+			       path);
 	if (mds_ctx == NULL) {
 		DEBUG(1, ("error in mds_init_ctx for: %s\n", path));
 		return NT_STATUS_UNSUCCESSFUL;
-- 
2.5.0


From d3a1e1ad9e06c4f513e16925a396d9f3f625105e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 27 Jan 2016 13:23:51 +0100
Subject: [PATCH 4/5] s3-mdssvc: use tevent_glib_glue in mdssvc RPC service

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/rpc_server/mdssvc/mdssvc.c | 244 +++++++++++++++----------------------
 source3/rpc_server/mdssvc/mdssvc.h |  14 ++-
 2 files changed, 110 insertions(+), 148 deletions(-)

diff --git a/source3/rpc_server/mdssvc/mdssvc.c b/source3/rpc_server/mdssvc/mdssvc.c
index 9d1f206..ca8f3d7 100644
--- a/source3/rpc_server/mdssvc/mdssvc.c
+++ b/source3/rpc_server/mdssvc/mdssvc.c
@@ -19,6 +19,7 @@
 */
 
 #include "includes.h"
+#include "messages.h"
 #include "librpc/gen_ndr/auth.h"
 #include "dbwrap/dbwrap.h"
 #include "lib/util/dlinklist.h"
@@ -26,6 +27,7 @@
 #include "lib/util/time_basic.h"
 #include "lib/dbwrap/dbwrap_rbt.h"
 #include "libcli/security/dom_sid.h"
+#include "rpc_server/mdssvc/srv_mdssvc_nt.h"
 #include "mdssvc.h"
 #include "sparql_parser.h"
 
@@ -65,6 +67,23 @@ struct slq_destroy_state {
 };
 
 /*
+ * Why must this be a local?
+ *
+ * Because the client will bind multiple times to the RPC service, one
+ * time for each tree connect and we don't want to end up with N*M (n
+ * client, m tree connects) connections to tracker.
+ *
+ * In the case of running the RPC service embedded (the default), this
+ * initialisation function is called from mds_init_ctx() which is
+ * called for every bind.
+ *
+ * In the case of running the RPC service external, we are called from
+ * mds_init() which is called right away when the RPC service process
+ * starts.
+ */
+static struct mdssvc_ctx *mdssvc_ctx;
+
+/*
  * If these functions return an error, they hit something like a non
  * recoverable talloc error. Most errors are dealt with by returning
  * an errror code in the Spotlight RPC reply.
@@ -731,7 +750,6 @@ static void tracker_con_cb(GObject *object,
 	}
 
 	DEBUG(10, ("connected to Tracker\n"));
-	g_main_loop_quit(mds_ctx->gmainloop);
 }
 
 static void tracker_cursor_cb_destroy_done(struct tevent_req *subreq);
@@ -770,7 +788,6 @@ static void tracker_cursor_cb(GObject *object,
 		 * we return.
 		 */
 		SLQ_DEBUG(10, slq, "closed");
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 
 		req = slq_destroy_send(slq, server_event_context(), &slq);
 		if (req == NULL) {
@@ -785,13 +802,11 @@ static void tracker_cursor_cb(GObject *object,
 		DEBUG(1, ("Tracker cursor: %s\n", error->message));
 		g_error_free(error);
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
 	if (!more_results) {
 		slq->state = SLQ_STATE_DONE;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
@@ -799,31 +814,48 @@ static void tracker_cursor_cb(GObject *object,
 	if (uri == NULL) {
 		DEBUG(1, ("error fetching Tracker URI\n"));
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 	path = tracker_to_unix_path(slq->query_results, uri);
 	if (path == NULL) {
 		DEBUG(1, ("error converting Tracker URI to path: %s\n", uri));
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
+	/*
+	 * We're in a tevent callback which means in the case of
+	 * running as external RPC service we're running as root and
+	 * not as the user.
+	 */
+	if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
+		DEBUG(0, ("can't become authenticated user: %d\n", slq->mds_ctx->uid));
+		smb_panic("can't become authenticated user");
+	}
+
 	if (geteuid() != slq->mds_ctx->uid) {
 		DEBUG(0, ("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid));
 		smb_panic("uid mismatch");
 	}
 
+	/*
+	 * We've changed identity to the authenticated pipe user, so
+	 * any function exit below must ensure we switch back
+	 */
+
 	result = sys_stat(path, &sb, false);
 	if (result != 0) {
+		unbecome_authenticated_pipe_user();
 		goto done;
 	}
 	result = access(path, R_OK);
 	if (result != 0) {
+		unbecome_authenticated_pipe_user();
 		goto done;
 	}
 
+	unbecome_authenticated_pipe_user();
+
 	ino64 = sb.st_ex_ino;
 	if (slq->cnids) {
 		/*
@@ -847,7 +879,6 @@ static void tracker_cursor_cb(GObject *object,
 	if (result != 0) {
 		DEBUG(1, ("dalloc error\n"));
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 	ok = add_filemeta(slq->reqinfo, slq->query_results->fm_array,
@@ -855,7 +886,6 @@ static void tracker_cursor_cb(GObject *object,
 	if (!ok) {
 		DEBUG(1, ("add_filemeta error\n"));
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
@@ -863,7 +893,6 @@ static void tracker_cursor_cb(GObject *object,
 	if (!ok) {
 		DEBUG(1, ("inode_map_add error\n"));
 		slq->state = SLQ_STATE_ERROR;
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
@@ -873,7 +902,6 @@ done:
 	if (slq->query_results->num_results >= MAX_SL_RESULTS) {
 		slq->state = SLQ_STATE_FULL;
 		SLQ_DEBUG(10, slq, "full");
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
@@ -910,13 +938,11 @@ static void tracker_query_cb(GObject *object,
 		slq->state = SLQ_STATE_ERROR;
 		DEBUG(1, ("Tracker query error: %s\n", error->message));
 		g_error_free(error);
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		return;
 	}
 
 	if (slq->state == SLQ_STATE_DONE) {
 		SLQ_DEBUG(10, slq, "done");
-		g_main_loop_quit(slq->mds_ctx->gmainloop);
 		talloc_free(slq);
 		return;
 	}
@@ -1273,13 +1299,11 @@ static bool slrpc_open_query(struct mds_ctx *mds_ctx,
 
 	DEBUG(10, ("SPARQL query: \"%s\"\n", slq->sparql_query));
 
-	g_main_context_push_thread_default(mds_ctx->gcontext);
 	tracker_sparql_connection_query_async(mds_ctx->tracker_con,
 					      slq->sparql_query,
 					      slq->gcancellable,
 					      tracker_query_cb,
 					      slq);
-	g_main_context_pop_thread_default(mds_ctx->gcontext);
 	slq->state = SLQ_STATE_RUNNING;
 
 	sl_result = 0;
@@ -1372,13 +1396,11 @@ static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
 		}
 		if (slq->state == SLQ_STATE_FULL) {
 			slq->state = SLQ_STATE_RESULTS;
-			g_main_context_push_thread_default(mds_ctx->gcontext);
 			tracker_sparql_cursor_next_async(
 				slq->tracker_cursor,
 				slq->gcancellable,
 				tracker_cursor_cb,
 				slq);
-			g_main_context_pop_thread_default(mds_ctx->gcontext);
 		}
 		break;
 
@@ -1782,9 +1804,38 @@ done:
 	return true;
 }
 
-/**
- * Init callbacks at startup, nothing to do here really
- **/
+static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
+{
+	if (mdssvc_ctx != NULL) {
+		return mdssvc_ctx;
+	}
+
+	mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
+	if (mdssvc_ctx == NULL) {
+		return NULL;
+	}
+
+	mdssvc_ctx->ev_ctx = ev;
+
+	mdssvc_ctx->gmain_ctx = g_main_context_new();
+	if (mdssvc_ctx->gmain_ctx == NULL) {
+		DBG_ERR("error from g_main_context_new\n");
+		return NULL;
+	}
+
+	g_main_context_push_thread_default(mdssvc_ctx->gmain_ctx);
+
+	mdssvc_ctx->glue = tevent_glib_glue_create(ev,
+						   mdssvc_ctx->ev_ctx,
+						   mdssvc_ctx->gmain_ctx);
+	if (mdssvc_ctx->glue == NULL) {
+		DBG_ERR("tevent_glib_glue_create failed\n");
+		return NULL;
+	}
+
+	return mdssvc_ctx;
+}
+
 bool mds_init(struct messaging_context *msg_ctx)
 {
 	return true;
@@ -1792,25 +1843,37 @@ bool mds_init(struct messaging_context *msg_ctx)
 
 bool mds_shutdown(void)
 {
+	if (mdssvc_ctx == NULL) {
+		return false;
+	}
+
+	tevent_glib_glue_quit(mdssvc_ctx->glue);
+	TALLOC_FREE(mdssvc_ctx->glue);
+
+	g_main_context_pop_thread_default(mdssvc_ctx->gmain_ctx);
+
+	TALLOC_FREE(mdssvc_ctx);
+
 	return true;
 }
 
-static gboolean gmainloop_timer(gpointer user_data)
+static void debug_fn(void *private_data,
+		     enum tevent_debug_level level,
+		     const char *fmt,
+		     va_list ap)
 {
-	struct mds_ctx *ctx = talloc_get_type_abort(user_data, struct mds_ctx);
-
-	DEBUG(10,("%s\n", __func__));
-	g_main_loop_quit(ctx->gmainloop);
-
-	return G_SOURCE_CONTINUE;
+	dbgtext_va(fmt, ap);
 }
 
 /**
- * Initialise a context per share handle
+ * Initialise a context per RPC bind
+ *
+ * This ends up being called for every tcon, because the client does a
+ * RPC bind for every tcon, so this is acually a per tcon context.
  **/
 struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
 			     struct tevent_context *ev,
-			     const struct auth_session_info *session_info,
+			     struct auth_session_info *session_info,
 			     const char *path)
 {
 	struct mds_ctx *mds_ctx;
@@ -1821,11 +1884,19 @@ struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
 	}
 	talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
 
+	mds_ctx->mdssvc_ctx = mdssvc_init(ev);
+	if (mds_ctx->mdssvc_ctx == NULL) {
+		goto error;
+	}
+	tevent_set_debug(ev, debug_fn, NULL);
+
 	mds_ctx->spath = talloc_strdup(mds_ctx, path);
 	if (mds_ctx->spath == NULL) {
 		goto error;
 	}
 
+	mds_ctx->pipe_session_info = session_info;
+
 	if (session_info->security_token->num_sids < 1) {
 		goto error;
 	}
@@ -1838,22 +1909,8 @@ struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
 		goto error;
 	}
 
-	mds_ctx->gcontext = g_main_context_new();
-	if (mds_ctx->gcontext == NULL) {
-		DEBUG(1,("error from g_main_context_new\n"));
-		goto error;
-	}
-
-	mds_ctx->gmainloop = g_main_loop_new(mds_ctx->gcontext, false);
-	if (mds_ctx->gmainloop == NULL) {
-		DEBUG(1,("error from g_main_loop_new\n"));
-		goto error;
-	}
-
-	g_main_context_push_thread_default(mds_ctx->gcontext);
 	tracker_sparql_connection_get_async(mds_ctx->gcancellable,
 					    tracker_con_cb, mds_ctx);
-	g_main_context_pop_thread_default(mds_ctx->gcontext);
 
 	return mds_ctx;
 
@@ -1886,76 +1943,12 @@ int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
 		g_cancellable_cancel(mds_ctx->gcancellable);
 		g_object_unref(mds_ctx->gcancellable);
 	}
-	if (mds_ctx->gmainloop != NULL) {
-		g_main_loop_unref(mds_ctx->gmainloop);
-	}
-	if (mds_ctx->gcontext != NULL) {
-		g_main_context_unref(mds_ctx->gcontext);
-	}
 
 	ZERO_STRUCTP(mds_ctx);
 
 	return 0;
 }
 
-static bool mds_run_gmainloop(struct mds_ctx *mds_ctx, guint timeout)
-{
-	guint timer_id;
-	GSource *timer;
-
-	/*
-	 * It seems the event processing of the libtracker-sparql
-	 * async subsystem defers callbacks until *all* events are
-	 * processes by the async subsystem main processing loop.
-	 *
-	 * g_main_context_iteration(may_block=FALSE) can't be used,
-	 * because a search that produces a few thousand matches
-	 * generates as many events that must be processed in either
-	 * g_main_context_iteration() or g_main_loop_run() before
-	 * callbacks are called.
-	 *
-	 * Unfortunately g_main_context_iteration() only processes a
-	 * small subset of these event (1-30) at a time when run in
-	 * mds_dispatch(), which happens once a second while the
-	 * client polls for results.
-	 *
-	 * Carefully using the blocking g_main_loop_run() fixes
-	 * this. It processes events until we exit from the loop at
-	 * defined exit points. By adding a 1 ms timeout we at least
-	 * try to get as close as possible to non-blocking behaviour.
-	 */
-
-	if (!g_main_context_pending(mds_ctx->gcontext)) {
-		return true;
-	}
-
-	g_main_context_push_thread_default(mds_ctx->gcontext);
-
-	timer = g_timeout_source_new(timeout);
-	if (timer == NULL) {
-		DEBUG(1,("g_timeout_source_new_seconds\n"));
-		g_main_context_pop_thread_default(mds_ctx->gcontext);
-		return false;
-	}
-
-	timer_id = g_source_attach(timer, mds_ctx->gcontext);
-	if (timer_id == 0) {
-		DEBUG(1,("g_timeout_add failed\n"));
-		g_source_destroy(timer);
-		g_main_context_pop_thread_default(mds_ctx->gcontext);
-		return false;
-	}
-
-	g_source_set_callback(timer, gmainloop_timer, mds_ctx, NULL);
-
-	g_main_loop_run(mds_ctx->gmainloop);
-
-	g_source_destroy(timer);
-
-	g_main_context_pop_thread_default(mds_ctx->gcontext);
-	return true;
-}
-
 /**
  * Dispatch a Spotlight RPC command
  **/
@@ -1980,34 +1973,6 @@ bool mds_dispatch(struct mds_ctx *mds_ctx,
 
 	response_blob->length = 0;
 
-	/*
-	 * Process finished glib events.
-	 *
-	 * FIXME: integrate with tevent instead of piggy packing it
-	 * onto the processing of new requests.
-	 *
-	 * mds_dispatch() is called by the client a few times in a row:
-	 *
-	 * - first in order to open/start a search query
-	 *
-	 * - later in order to fetch results asynchronously, typically
-	 *   once a second. If no results have been retrieved from the
-	 *   search store (Tracker) yet, we return no results.
-	 *   The client asks for more results every second as long
-	 *   as the "Search Window" in the client gui is open.
-	 *
-	 * - at some point the query is closed
-	 *
-	 * This means we try to iterate through the glib event loop
-	 * before processing the request in order to get result
-	 * from tracker which can be returned to the client.
-	 */
-
-	ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS);
-	if (!ok) {
-		goto cleanup;
-	}
-
 	DEBUG(10, ("share path: %s\n", mds_ctx->spath));
 
 	query = dalloc_new(mds_ctx);
@@ -2068,17 +2033,6 @@ bool mds_dispatch(struct mds_ctx *mds_ctx,
 		goto cleanup;
 	}
 
-	/*
-	 * Run g_main_loop a second time in order to dispatch events
-	 * that may have been queued at the libtracker-sparql level.
-	 * As we only want to dispatch (write out requests) but not
-	 * wait for anything, we use a much shorter timeout here.
-	 */
-	ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS / 10);
-	if (!ok) {
-		goto cleanup;
-	}
-
 	response_blob->length = len;
 
 cleanup:
diff --git a/source3/rpc_server/mdssvc/mdssvc.h b/source3/rpc_server/mdssvc/mdssvc.h
index 3bbcdd6..469ae90 100644
--- a/source3/rpc_server/mdssvc/mdssvc.h
+++ b/source3/rpc_server/mdssvc/mdssvc.h
@@ -92,14 +92,22 @@ struct sl_inode_path_map {
 	char              *path;
 };
 
+/* Per process state */
+struct mdssvc_ctx {
+	struct tevent_context *ev_ctx;
+	GMainContext *gmain_ctx;
+	struct tevent_glib_glue *glue;
+};
+
+/* Per tree connect state */
 struct mds_ctx {
+	struct mdssvc_ctx *mdssvc_ctx;
+	struct auth_session_info *pipe_session_info;
 	struct dom_sid sid;
 	uid_t uid;
 	const char *spath;
 	GCancellable *gcancellable;
 	TrackerSparqlConnection *tracker_con;
-	GMainContext *gcontext;
-	GMainLoop *gmainloop;
 	struct sl_query *query_list;     /* list of active queries */
 	struct db_context *ino_path_map; /* dbwrap rbt for storing inode->path mappings */
 };
@@ -115,7 +123,7 @@ extern bool mds_init(struct messaging_context *msg_ctx);
 extern bool mds_shutdown(void);
 extern struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
 				    struct tevent_context *ev,
-				    const struct auth_session_info *session_info,
+				    struct auth_session_info *session_info,
 				    const char *path);
 extern int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx);
 extern bool mds_dispatch(struct mds_ctx *query_ctx,
-- 
2.5.0


From 88748e36dfb0f315020c12ad21e7bba94923dc50 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 28 Jan 2016 08:29:28 +0100
Subject: [PATCH 5/5] tevent: add a tevent_glib_glue test

Tests adapted from glib2 glib/tests/mainloop.c.

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 lib/tevent/testsuite.c              | 349 ++++++++++++++++++++++++++++++++++++
 source4/torture/local/wscript_build |   3 +
 2 files changed, 352 insertions(+)

diff --git a/lib/tevent/testsuite.c b/lib/tevent/testsuite.c
index bcd27fd..6d862d1 100644
--- a/lib/tevent/testsuite.c
+++ b/lib/tevent/testsuite.c
@@ -5,6 +5,10 @@
 
    Copyright (C) Stefan Metzmacher 2006-2009
    Copyright (C) Jeremy Allison    2013
+   Copyright (C) Ralph Boehme      2016
+
+   glib tests adapted from glib2 glib/tests/mainloop.c
+   Copyright (C) 2011 Red Hat Inc., Matthias Clasen
 
      ** NOTE! The following LGPL license applies to the tevent
      ** library. This does NOT imply that all of Samba is released
@@ -35,6 +39,10 @@
 #include <pthread.h>
 #include <assert.h>
 #endif
+#ifdef HAVE_GLIB
+#include <glib.h>
+#include <glib-unix.h>
+#endif
 
 static int fde_count;
 
@@ -1131,6 +1139,342 @@ static bool test_multi_tevent_threaded_1(struct torture_context *test,
 }
 #endif
 
+#ifdef HAVE_GLIB
+/*
+ * Unfortunately the glib test suite runner doesn't pass args to tests
+ * so we must keep a few globals here.
+ */
+static struct tevent_context *ev;
+
+static gboolean cb (gpointer data)
+{
+	return FALSE;
+}
+
+static gboolean prepare (GSource *source, gint *time)
+{
+	return FALSE;
+}
+static gboolean check (GSource *source)
+{
+	return FALSE;
+}
+static gboolean dispatch (GSource *source, GSourceFunc cb_in, gpointer date)
+{
+	return FALSE;
+}
+
+static GSourceFuncs funcs = {
+	prepare,
+	check,
+	dispatch,
+	NULL
+};
+
+static void test_maincontext_basic(void)
+{
+	GMainContext *ctx;
+	struct tevent_glib_glue *glue;
+	GSource *source;
+	guint id;
+	gpointer data = &funcs;
+
+	ctx = g_main_context_new ();
+	glue = tevent_glib_glue_create(ev, ev, ctx);
+	g_assert (glue != NULL);
+
+	g_assert (!g_main_context_pending (ctx));
+	g_assert (!g_main_context_iteration (ctx, FALSE));
+
+	source = g_source_new (&funcs, sizeof (GSource));
+	g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_DEFAULT);
+	g_assert (!g_source_is_destroyed (source));
+
+	g_assert (!g_source_get_can_recurse (source));
+	g_assert (g_source_get_name (source) == NULL);
+
+	g_source_set_can_recurse (source, TRUE);
+	g_source_set_name (source, "d");
+
+	g_assert (g_source_get_can_recurse (source));
+	g_assert_cmpstr (g_source_get_name (source), ==, "d");
+
+	g_assert (g_main_context_find_source_by_user_data (ctx, NULL) == NULL);
+	g_assert (g_main_context_find_source_by_funcs_user_data (ctx, &funcs, NULL) == NULL);
+
+	id = g_source_attach (source, ctx);
+	g_assert_cmpint (g_source_get_id (source), ==, id);
+	g_assert (g_main_context_find_source_by_id (ctx, id) == source);
+
+	g_source_set_priority (source, G_PRIORITY_HIGH);
+	g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_HIGH);
+
+	g_source_destroy (source);
+	g_assert (g_source_get_context (source) == ctx);
+	g_assert (g_main_context_find_source_by_id (ctx, id) == NULL);
+
+	tevent_glib_glue_quit(glue);
+	TALLOC_FREE(glue);
+	g_main_context_unref (ctx);
+
+	if (g_test_undefined ())
+	{
+		g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+				       "*assertion*source->context != NULL*failed*");
+		g_assert (g_source_get_context (source) == NULL);
+		g_test_assert_expected_messages ();
+	}
+
+	g_source_unref (source);
+
+	ctx = g_main_context_default ();
+
+	glue = tevent_glib_glue_create(ev, ev, ctx);
+	g_assert (glue != NULL);
+
+	source = g_source_new (&funcs, sizeof (GSource));
+	g_source_set_funcs (source, &funcs);
+	g_source_set_callback (source, cb, data, NULL);
+	id = g_source_attach (source, ctx);
+	g_source_unref (source);
+	g_source_set_name_by_id (id, "e");
+	g_assert_cmpstr (g_source_get_name (source), ==, "e");
+	g_assert (g_source_get_context (source) == ctx);
+	g_assert (g_source_remove_by_funcs_user_data (&funcs, data));
+
+	source = g_source_new (&funcs, sizeof (GSource));
+	g_source_set_funcs (source, &funcs);
+	g_source_set_callback (source, cb, data, NULL);
+	id = g_source_attach (source, ctx);
+	g_source_unref (source);
+	g_assert (g_source_remove_by_user_data (data));
+	g_assert (!g_source_remove_by_user_data ((gpointer)0x1234));
+
+	g_idle_add (cb, data);
+	g_assert (g_idle_remove_by_data (data));
+
+	tevent_glib_glue_quit(glue);
+	TALLOC_FREE(glue);
+}
+
+static gboolean count_calls (gpointer data)
+{
+	gint *i = data;
+
+	(*i)++;
+
+	return TRUE;
+}
+
+static gboolean quit_loop (gpointer data)
+{
+	struct tevent_glib_glue *glue = talloc_get_type_abort(data, struct tevent_glib_glue);
+
+	tevent_glib_glue_quit(glue);
+
+	return G_SOURCE_REMOVE;
+}
+
+static void test_timeouts (void)
+{
+	GMainContext *ctx;
+	struct tevent_glib_glue *glue;
+	GSource *source;
+	static gint a;
+	static gint b;
+	static gint c;
+
+	a = b = c = 0;
+
+	ctx = g_main_context_new ();
+	glue = tevent_glib_glue_create(ev, ev, ctx);
+	g_assert (glue != NULL);
+
+	source = g_timeout_source_new (100);
+	g_source_set_callback (source, count_calls, &a, NULL);
+	g_source_attach (source, ctx);
+	g_source_unref (source);
+
+	source = g_timeout_source_new (250);
+	g_source_set_callback (source, count_calls, &b, NULL);
+	g_source_attach (source, ctx);
+	g_source_unref (source);
+
+	source = g_timeout_source_new (330);
+	g_source_set_callback (source, count_calls, &c, NULL);
+	g_source_attach (source, ctx);
+	g_source_unref (source);
+
+	source = g_timeout_source_new (1050);
+	g_source_set_callback (source, quit_loop, glue, NULL);
+	g_source_attach (source, ctx);
+	g_source_unref (source);
+
+	g_assert (tevent_loop_wait(ev) == 0);
+
+	/* We may be delayed for an arbitrary amount of time - for example,
+	 * it's possible for all timeouts to fire exactly once.
+	 */
+	g_assert_cmpint (a, >, 0);
+	g_assert_cmpint (a, >=, b);
+	g_assert_cmpint (b, >=, c);
+
+	g_assert_cmpint (a, <=, 10);
+	g_assert_cmpint (b, <=, 4);
+	g_assert_cmpint (c, <=, 3);
+
+	tevent_glib_glue_quit(glue);
+	TALLOC_FREE(glue);
+	g_main_context_unref (ctx);
+}
+
+
+static gchar zeros[1024];
+
+static gsize fill_a_pipe (gint fd)
+{
+	gsize written = 0;
+	GPollFD pfd;
+
+	pfd.fd = fd;
+	pfd.events = G_IO_OUT;
+	while (g_poll (&pfd, 1, 0) == 1)
+		/* we should never see -1 here */
+		written += write (fd, zeros, sizeof zeros);
+
+	return written;
+}
+
+static gboolean write_bytes (gint	  fd,
+			     GIOCondition condition,
+			     gpointer	  user_data)
+{
+	gssize *to_write = user_data;
+	gint limit;
+
+	if (*to_write == 0)
+		return FALSE;
+
+	/* Detect if we run before we should */
+	g_assert (*to_write >= 0);
+
+	limit = MIN (*to_write, sizeof zeros);
+	*to_write -= write (fd, zeros, limit);
+
+	return TRUE;
+}
+
+static gboolean read_bytes (gint	 fd,
+			    GIOCondition condition,
+			    gpointer	 user_data)
+{
+	static gchar buffer[1024];
+	gssize *to_read = user_data;
+
+	*to_read -= read (fd, buffer, sizeof buffer);
+
+	/* The loop will exit when there is nothing else to read, then we will
+	 * use g_source_remove() to destroy this source.
+	 */
+	return TRUE;
+}
+
+static void test_unix_fd(void)
+{
+	gssize to_write = -1;
+	gssize to_read;
+	gint fds[2];
+	gint a, b;
+	gint s;
+	GSource *source_a;
+	GSource *source_b;
+	struct tevent_glib_glue *glue;
+
+	glue = tevent_glib_glue_create(ev, ev, g_main_context_default());
+	g_assert (glue != NULL);
+
+	s = pipe (fds);
+	g_assert (s == 0);
+
+	to_read = fill_a_pipe (fds[1]);
+	/* write at higher priority to keep the pipe full... */
+	a = g_unix_fd_add_full (G_PRIORITY_HIGH, fds[1], G_IO_OUT, write_bytes, &to_write, NULL);
+	source_a = g_source_ref (g_main_context_find_source_by_id (NULL, a));
+	/* make sure no 'writes' get dispatched yet */
+	while (tevent_loop_once(ev));
+
+	to_read += 128 * 1024 * 1024;
+	to_write = 128 * 1024 * 1024;
+	b = g_unix_fd_add (fds[0], G_IO_IN, read_bytes, &to_read);
+	source_b = g_source_ref (g_main_context_find_source_by_id (NULL, b));
+
+	/* Assuming the kernel isn't internally 'laggy' then there will always
+	 * be either data to read or room in which to write.  That will keep
+	 * the loop running until all data has been read and written.
+	 */
+	while (to_write > 0 || to_read > 0)
+	{
+		gssize to_write_was = to_write;
+		gssize to_read_was = to_read;
+
+		if (tevent_loop_once(ev) != 0)
+			break;
+
+		/* Since the sources are at different priority, only one of them
+		 * should possibly have run.
+		 */
+		g_assert (to_write == to_write_was || to_read == to_read_was);
+	}
+
+	g_assert (to_write == 0);
+	g_assert (to_read == 0);
+
+	/* 'a' is already removed by itself */
+	g_assert (g_source_is_destroyed (source_a));
+	g_source_unref (source_a);
+	g_source_remove (b);
+	g_assert (g_source_is_destroyed (source_b));
+	g_source_unref (source_b);
+
+	tevent_glib_glue_quit(glue);
+	TALLOC_FREE(glue);
+
+	close (fds[1]);
+	close (fds[0]);
+}
+
+static bool test_glib_glue(struct torture_context *test,
+			   const void *test_data)
+{
+	bool ok = true;
+	int ret;
+	int test_argc = 3;
+	char *test_argv[] = {
+		discard_const("test_glib_glue"),
+		discard_const("-m"),
+		discard_const("no-undefined")
+	};
+	char **argvp = test_argv;
+
+	g_test_init(&test_argc, &argvp, NULL);
+
+	ev = tevent_context_init(NULL);
+	torture_assert_not_null_goto(test, ev, ok, done, "tevent_context_init failed");
+
+	tevent_set_debug_stderr(ev);
+
+	g_test_add_func ("/maincontext/basic", test_maincontext_basic);
+	g_test_add_func ("/mainloop/timeouts", test_timeouts);
+	g_test_add_func ("/mainloop/unix-fd", test_unix_fd);
+
+	ret = g_test_run();
+	torture_assert_goto(test, ret == 0, ok, done, "A glib test failed\n");
+
+done:
+	return ok;
+}
+#endif
+
 struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx)
 {
 	struct torture_suite *suite = torture_suite_create(mem_ctx, "event");
@@ -1173,5 +1517,10 @@ struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx)
 
 #endif
 
+#ifdef HAVE_GLIB
+	torture_suite_add_simple_tcase_const(suite, "glib_glue",
+					     test_glib_glue,
+					     NULL);
+#endif
 	return suite;
 }
diff --git a/source4/torture/local/wscript_build b/source4/torture/local/wscript_build
index eb45df8..6c03ca9 100644
--- a/source4/torture/local/wscript_build
+++ b/source4/torture/local/wscript_build
@@ -23,6 +23,9 @@ TORTURE_LOCAL_SOURCE = '''../../../lib/util/charset/tests/iconv.c
 
 TORTURE_LOCAL_DEPS = 'RPC_NDR_ECHO TDR LIBCLI_SMB MESSAGING iconv POPT_CREDENTIALS TORTURE_AUTH TORTURE_UTIL TORTURE_NDR TORTURE_LIBCRYPTO share torture_registry PROVISION ldb samdb replace-test RPC_FSS_STATE'
 
+if bld.CONFIG_SET('HAVE_GLIB'):
+    TORTURE_LOCAL_DEPS += ' glib-2.0'
+
 bld.SAMBA_MODULE('TORTURE_LOCAL',
 	source=TORTURE_LOCAL_SOURCE,
 	autoproto='proto.h',
-- 
2.5.0



More information about the samba-technical mailing list