smbd: Protect ea-reading on symlinks
[Samba.git] / source3 / lib / tevent_glib_glue.c
blob3be08fc3794b3cd7629046e5116d7fbc570e53fe
1 /*
2 Unix SMB/CIFS implementation.
3 Integration of a glib g_main_context into a tevent_context
4 Copyright (C) Stefan Metzmacher 2016
5 Copyright (C) Ralph Boehme 2016
7 ** NOTE! The following LGPL license applies to the tevent
8 ** library. This does NOT imply that all of Samba is released
9 ** under the LGPL
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 3 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 #include "replace.h"
26 #include "system/filesys.h"
27 #include "lib/util/debug.h"
28 #include "lib/util/select.h"
29 #include <tevent.h>
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_TEVENT
34 #ifdef HAVE_GLIB
35 #include <glib.h>
36 #include "tevent_glib_glue.h"
38 struct fd_map {
39 struct tevent_glib_glue *glue;
40 int fd;
41 struct tevent_fd *fd_event;
44 struct tevent_glib_glue {
46 * The tevent context we're feeding.
48 struct tevent_context *ev;
51 * The glib gmain context we're polling and whether we're currently
52 * owning it by virtue of g_main_context_acquire().
54 GMainContext *gmain_ctx;
55 bool gmain_owner;
58 * Set by samba_tevent_glib_glue_quit().
60 bool quit;
63 * tevent trace callback and data we got from tevent_get_trace_callback()
64 * before installing our own trace callback.
66 tevent_trace_callback_t prev_tevent_trace_cb;
67 void *prev_tevent_trace_data;
70 * Don't call tevent_glib_prepare() in the tevent tracepoint handler if
71 * explicitly told so. This is an optimisation for the case that glib
72 * event sources are created from glib event callbacks.
74 bool skip_glib_refresh;
77 * Used when acquiring the glib gmain context failed.
79 struct tevent_timer *acquire_retry_timer;
82 * glib gmain context timeout and priority for the current event look
83 * iteration. gtimeout is translated to a tevent timer event, unless it
84 * is 0 which signals some event source is pending. In that case we
85 * dispatch an immediate event. gpriority is ignored by us, just passed
86 * to the glib relevant functions.
88 gint gtimeout;
89 gint gpriority;
90 struct tevent_timer *timer;
91 struct tevent_immediate *im;
92 bool scheduled_im;
95 * glib gmain context fds returned from g_main_context_query(). These
96 * get translated to tevent fd events.
98 GPollFD *gpollfds;
99 gint num_gpollfds;
102 * A copy of gpollfds and num_gpollfds from the previous event loop
103 * iteration, used to detect changes in the set of fds.
105 GPollFD *prev_gpollfds;
106 gint num_prev_gpollfds;
109 * An array of pointers to fd_map's. The fd_map'd contain the tevent
110 * event fd as well as a pointer to the corresponding glib GPollFD.
112 struct fd_map **fd_map;
113 size_t num_maps;
116 static bool tevent_glib_prepare(struct tevent_glib_glue *glue);
117 static bool tevent_glib_process(struct tevent_glib_glue *glue);
118 static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue);
119 static void tevent_glib_fd_handler(struct tevent_context *ev,
120 struct tevent_fd *fde,
121 uint16_t flags,
122 void *private_data);
124 typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2);
125 typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue,
126 const GPollFD *new,
127 const GPollFD *old);
128 typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue,
129 const GPollFD *fd);
130 typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue,
131 const GPollFD *fd);
134 * Compare two sorted GPollFD arrays
136 * For every element that exists in gfds and prev_gfds found_fn() is called.
137 * For every element in gfds but not in prev_gfds, new_fn() is called.
138 * For every element in prev_gfds but not in gfds removed_fn() is called.
140 static bool cmp_gfds(struct tevent_glib_glue *glue,
141 GPollFD *gfds,
142 GPollFD *prev_gfds,
143 size_t num_gfds,
144 size_t num_prev_gfds,
145 gfds_cmp_cb cmp_cb,
146 gfds_found_cb found_cb,
147 gfds_new_cb new_cb,
148 gfds_removed_cb removed_cb)
150 bool ok;
151 size_t i = 0, j = 0;
152 int cmp;
154 while (i < num_gfds && j < num_prev_gfds) {
155 cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
156 if (cmp == 0) {
157 ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
158 if (!ok) {
159 return false;
161 i++;
162 j++;
163 } else if (cmp < 0) {
164 ok = new_cb(glue, &gfds[i]);
165 if (!ok) {
166 return false;
168 i++;
169 } else {
170 ok = removed_cb(glue, &prev_gfds[j]);
171 if (!ok) {
172 return false;
174 j++;
178 while (i < num_gfds) {
179 ok = new_cb(glue, &gfds[i++]);
180 if (!ok) {
181 return false;
185 while (j < num_prev_gfds) {
186 ok = removed_cb(glue, &prev_gfds[j++]);
187 if (!ok) {
188 return false;
192 return true;
195 static int glib_fd_cmp_func(const void *p1, const void *p2)
197 const GPollFD *lhs = p1;
198 const GPollFD *rhs = p2;
200 if (lhs->fd < rhs->fd) {
201 return -1;
202 } else if (lhs->fd > rhs->fd) {
203 return 1;
206 return 0;
210 * We already have a tevent fd event for the glib GPollFD, but we may have to
211 * update flags.
213 static bool match_gfd_cb(struct tevent_glib_glue *glue,
214 const GPollFD *new_gfd,
215 const GPollFD *old_gfd)
217 size_t i;
218 struct fd_map *fd_map = NULL;
219 struct tevent_fd *fd_event = NULL;
221 if (new_gfd->events == old_gfd->events) {
222 return true;
225 for (i = 0; i < glue->num_maps; i++) {
226 if (glue->fd_map[i]->fd == new_gfd->fd) {
227 break;
231 if (i == glue->num_maps) {
232 DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
233 return false;
236 fd_map = glue->fd_map[i];
237 if (fd_map == NULL) {
238 DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd);
239 return false;
242 fd_event = fd_map->fd_event;
243 if (fd_event == NULL) {
244 DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd);
245 return false;
248 tevent_fd_set_flags(fd_event, 0);
250 if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
251 TEVENT_FD_READABLE(fd_event);
253 if (new_gfd->events & G_IO_OUT) {
254 TEVENT_FD_WRITEABLE(fd_event);
257 return true;
260 static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
262 struct tevent_fd *fd_event = NULL;
263 struct fd_map *fd_map = NULL;
264 uint16_t events = 0;
265 bool revent;
266 bool wevent;
268 revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR));
269 wevent = (gfd->events & G_IO_OUT);
270 if (revent) {
271 events |= TEVENT_FD_READ;
273 if (wevent) {
274 events |= TEVENT_FD_WRITE;
277 glue->fd_map = talloc_realloc(glue,
278 glue->fd_map,
279 struct fd_map *,
280 glue->num_maps + 1);
281 if (glue->fd_map == NULL) {
282 DBG_ERR("talloc_realloc failed\n");
283 return false;
285 fd_map = talloc_zero(glue->fd_map, struct fd_map);
286 if (fd_map == NULL) {
287 DBG_ERR("talloc_realloc failed\n");
288 return false;
290 glue->fd_map[glue->num_maps] = fd_map;
291 glue->num_maps++;
293 fd_event = tevent_add_fd(glue->ev,
294 glue->fd_map,
295 gfd->fd,
296 events,
297 tevent_glib_fd_handler,
298 fd_map);
299 if (fd_event == NULL) {
300 DBG_ERR("tevent_add_fd failed\n");
301 return false;
304 *fd_map = (struct fd_map) {
305 .glue = glue,
306 .fd = gfd->fd,
307 .fd_event = fd_event,
310 DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
312 return true;
315 static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
317 size_t i;
319 for (i = 0; i < glue->num_maps; i++) {
320 if (glue->fd_map[i]->fd == gfd->fd) {
321 break;
325 if (i == glue->num_maps) {
326 DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
327 return false;
330 TALLOC_FREE(glue->fd_map[i]->fd_event);
331 TALLOC_FREE(glue->fd_map[i]);
333 if (i + 1 < glue->num_maps) {
334 memmove(&glue->fd_map[i],
335 &glue->fd_map[i+1],
336 (glue->num_maps - (i + 1)) * sizeof(struct fd_map *));
339 glue->fd_map = talloc_realloc(glue,
340 glue->fd_map,
341 struct fd_map *,
342 glue->num_maps - 1);
343 if (glue->num_maps > 0 && glue->fd_map == NULL) {
344 DBG_ERR("talloc_realloc failed\n");
345 return false;
347 glue->num_maps--;
349 return true;
352 static short gpoll_to_poll_event(gushort gevent)
354 short pevent = 0;
356 if (gevent & G_IO_IN) {
357 pevent |= POLLIN;
359 if (gevent & G_IO_OUT) {
360 pevent |= POLLOUT;
362 if (gevent & G_IO_HUP) {
363 pevent |= POLLHUP;
365 if (gevent & G_IO_ERR) {
366 pevent |= POLLERR;
369 return pevent;
372 static gushort poll_to_gpoll_event(short pevent)
374 gushort gevent = 0;
376 if (pevent & POLLIN) {
377 gevent |= G_IO_IN;
379 if (pevent & POLLOUT) {
380 gevent |= G_IO_OUT;
382 if (pevent & POLLHUP) {
383 gevent |= G_IO_HUP;
385 if (pevent & POLLERR) {
386 gevent |= G_IO_ERR;
389 return gevent;
392 static void tevent_glib_fd_handler(struct tevent_context *ev,
393 struct tevent_fd *fde,
394 uint16_t flags,
395 void *private_data)
397 struct fd_map *fd_map = talloc_get_type_abort(
398 private_data, struct fd_map);
399 struct tevent_glib_glue *glue = NULL;
400 GPollFD *gpollfd = NULL;
401 struct pollfd fd;
402 int ret;
403 int i;
405 glue = fd_map->glue;
407 for (i = 0; i < glue->num_gpollfds; i++) {
408 if (glue->gpollfds[i].fd != fd_map->fd) {
409 continue;
411 gpollfd = &glue->gpollfds[i];
412 break;
414 if (gpollfd == NULL) {
415 DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
416 fd_map, fd_map->fd);
417 return;
420 * We have to poll() the fd to get the correct fd event for glib. tevent
421 * only tells us about readable/writable in flags, but we need the full
422 * glory for glib.
425 fd = (struct pollfd) {
426 .fd = gpollfd->fd,
427 .events = gpoll_to_poll_event(gpollfd->events),
430 ret = sys_poll_intr(&fd, 1, 0);
431 if (ret == -1) {
432 DBG_ERR("poll: %s\n", strerror(errno));
433 return;
435 if (ret == 0) {
436 return;
439 gpollfd->revents = poll_to_gpoll_event(fd.revents);
441 tevent_glib_process(glue);
442 return;
445 static void tevent_glib_timer_handler(struct tevent_context *ev,
446 struct tevent_timer *te,
447 struct timeval current_time,
448 void *private_data)
450 struct tevent_glib_glue *glue = talloc_get_type_abort(
451 private_data, struct tevent_glib_glue);
453 glue->timer = NULL;
454 tevent_glib_process(glue);
455 return;
458 static void tevent_glib_im_handler(struct tevent_context *ev,
459 struct tevent_immediate *im,
460 void *private_data)
462 struct tevent_glib_glue *glue = talloc_get_type_abort(
463 private_data, struct tevent_glib_glue);
465 glue->scheduled_im = false;
466 tevent_glib_process(glue);
467 return;
470 static bool save_current_fdset(struct tevent_glib_glue *glue)
473 * Save old glib fds. We only grow the prev array.
476 if (glue->num_prev_gpollfds < glue->num_gpollfds) {
477 glue->prev_gpollfds = talloc_realloc(glue,
478 glue->prev_gpollfds,
479 GPollFD,
480 glue->num_gpollfds);
481 if (glue->prev_gpollfds == NULL) {
482 DBG_ERR("talloc_realloc failed\n");
483 return false;
486 glue->num_prev_gpollfds = glue->num_gpollfds;
487 if (glue->num_gpollfds > 0) {
488 memcpy(glue->prev_gpollfds, glue->gpollfds,
489 sizeof(GPollFD) * glue->num_gpollfds);
490 memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
493 return true;
496 static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
498 bool ok;
499 gint num_fds;
501 ok = save_current_fdset(glue);
502 if (!ok) {
503 return false;
506 while (true) {
507 num_fds = g_main_context_query(glue->gmain_ctx,
508 glue->gpriority,
509 &glue->gtimeout,
510 glue->gpollfds,
511 glue->num_gpollfds);
512 if (num_fds == glue->num_gpollfds) {
513 break;
515 glue->gpollfds = talloc_realloc(glue,
516 glue->gpollfds,
517 GPollFD,
518 num_fds);
519 if (num_fds > 0 && glue->gpollfds == NULL) {
520 DBG_ERR("talloc_realloc failed\n");
521 return false;
523 glue->num_gpollfds = num_fds;
526 if (glue->num_gpollfds > 0) {
527 qsort(glue->gpollfds,
528 num_fds,
529 sizeof(GPollFD),
530 glib_fd_cmp_func);
533 DBG_DEBUG("num fds: %d, timeout: %d ms\n",
534 num_fds, glue->gtimeout);
536 return true;
539 static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
541 struct timeval tv;
542 bool ok;
544 ok = cmp_gfds(glue,
545 glue->gpollfds,
546 glue->prev_gpollfds,
547 glue->num_gpollfds,
548 glue->num_prev_gpollfds,
549 glib_fd_cmp_func,
550 match_gfd_cb,
551 new_gfd_cb,
552 remove_gfd_cb);
553 if (!ok) {
554 return false;
557 TALLOC_FREE(glue->timer);
559 if (glue->gtimeout == -1) {
560 return true;
563 if (glue->gtimeout == 0) {
565 * glue->gtimeout is 0 if g_main_context_query() returned
566 * timeout=0. That means there are pending events ready to be
567 * dispatched. We only want to run one event handler per loop
568 * iteration, so we schedule an immediate event to run it in the
569 * next iteration.
571 if (glue->scheduled_im) {
572 return true;
574 tevent_schedule_immediate(glue->im,
575 glue->ev,
576 tevent_glib_im_handler,
577 glue);
578 glue->scheduled_im = true;
579 return true;
582 tv = tevent_timeval_current_ofs(glue->gtimeout / 1000,
583 (glue->gtimeout % 1000) * 1000);
585 glue->timer = tevent_add_timer(glue->ev,
586 glue,
588 tevent_glib_timer_handler,
589 glue);
590 if (glue->timer == NULL) {
591 DBG_ERR("tevent_add_timer failed\n");
592 return false;
595 return true;
598 static void tevent_glib_retry_timer(struct tevent_context *ev,
599 struct tevent_timer *te,
600 struct timeval current_time,
601 void *private_data)
603 struct tevent_glib_glue *glue = talloc_get_type_abort(
604 private_data, struct tevent_glib_glue);
606 glue->acquire_retry_timer = NULL;
607 (void)tevent_glib_prepare(glue);
611 * Fetch glib event sources and add them to tevent
613 * Fetch glib event sources and attach corresponding tevent events to our tevent
614 * context. get_glib_fds_and_timeout() gets the relevant glib event sources: the
615 * set of active fds and the next timer. tevent_glib_update_events() then
616 * translates those to tevent and creates tevent events.
618 * When called, the thread must NOT be the owner to the glib main
619 * context. tevent_glib_prepare() is either the first function when the
620 * tevent_glib_glue is created, or after tevent_glib_process() has been called
621 * to process pending event, which will have ceased ownership.
623 static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
625 bool ok;
626 gboolean gok;
628 if (glue->quit) {
629 /* Set via samba_tevent_glib_glue_quit() */
630 return true;
633 if (glue->acquire_retry_timer != NULL) {
635 * We're still waiting on the below g_main_context_acquire() to
636 * succeed, just return.
638 return true;
641 if (glue->gmain_owner) {
642 g_main_context_release(glue->gmain_ctx);
643 glue->gmain_owner = false;
646 gok = g_main_context_acquire(glue->gmain_ctx);
647 if (!gok) {
648 DBG_ERR("couldn't acquire g_main_context\n");
651 * Ensure no tevent event fires while we're not the gmain
652 * context owner. The event handler would call
653 * tevent_glib_process() and that expects being the owner of the
654 * context.
656 ok = tevent_glib_glue_reinit(glue);
657 if (!ok) {
658 DBG_ERR("tevent_glib_glue_reinit failed\n");
659 samba_tevent_glib_glue_quit(glue);
660 return false;
663 glue->acquire_retry_timer = tevent_add_timer(
664 glue->ev,
665 glue,
666 tevent_timeval_current_ofs(0, 1000),
667 tevent_glib_retry_timer,
668 glue);
669 if (glue->acquire_retry_timer == NULL) {
670 DBG_ERR("tevent_add_timer failed\n");
671 samba_tevent_glib_glue_quit(glue);
672 return false;
674 return true;
676 glue->gmain_owner = true;
679 * Discard "ready" return value from g_main_context_prepare(). We don't
680 * want to dispatch events here, that's only done in from the tevent loop.
682 (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
684 ok = get_glib_fds_and_timeout(glue);
685 if (!ok) {
686 DBG_ERR("get_glib_fds_and_timeout failed\n");
687 samba_tevent_glib_glue_quit(glue);
688 return false;
691 ok = tevent_glib_update_events(glue);
692 if (!ok) {
693 DBG_ERR("tevent_glib_update_events failed\n");
694 samba_tevent_glib_glue_quit(glue);
695 return false;
698 return true;
702 * Process pending glib events
704 * tevent_glib_process() gets called to process pending glib events via
705 * g_main_context_check() and then g_main_context_dispatch().
707 * After pending event handlers are dispatched, we rearm the glib glue event
708 * handlers in tevent by calling tevent_glib_prepare().
710 * When tevent_glib_process() is called the thread must own the glib
711 * gmain_ctx. That is achieved by tevent_glib_prepare() being the only function
712 * that acquires context ownership.
714 * To give other threads that are blocked on g_main_context_acquire(gmain_ctx) a
715 * chance to acquire context ownership (eg needed to attach event sources), we
716 * release context ownership before calling tevent_glib_prepare() which will
717 * acquire it again.
719 static bool tevent_glib_process(struct tevent_glib_glue *glue)
721 bool ok;
723 DBG_DEBUG("tevent_glib_process\n");
726 * Ignore the "sources_ready" return from g_main_context_check(). glib
727 * itself also ignores it in g_main_context_iterate(). In theory only
728 * calling g_main_context_dispatch() if g_main_context_check() returns
729 * true should work, but older glib versions had a bug where
730 * g_main_context_check() returns false even though events are pending.
732 * https://bugzilla.gnome.org/show_bug.cgi?id=11059
734 (void)g_main_context_check(glue->gmain_ctx,
735 glue->gpriority,
736 glue->gpollfds,
737 glue->num_gpollfds);
739 g_main_context_dispatch(glue->gmain_ctx);
741 ok = tevent_glib_prepare(glue);
742 if (!ok) {
743 return false;
745 glue->skip_glib_refresh = true;
746 return true;
749 static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
750 void *private_data)
752 struct tevent_glib_glue *glue = talloc_get_type_abort(
753 private_data, struct tevent_glib_glue);
755 if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
756 if (!glue->skip_glib_refresh) {
757 tevent_glib_prepare(glue);
759 glue->skip_glib_refresh = false;
762 /* chain previous handler */
763 if (glue->prev_tevent_trace_cb != NULL) {
764 glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
768 static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
770 size_t n = talloc_array_length(glue->fd_map);
771 size_t i;
773 for (i = 0; i < n; i++) {
774 TALLOC_FREE(glue->fd_map[i]->fd_event);
775 TALLOC_FREE(glue->fd_map[i]);
778 tevent_set_trace_callback(glue->ev,
779 glue->prev_tevent_trace_cb,
780 glue->prev_tevent_trace_data);
781 glue->prev_tevent_trace_cb = NULL;
782 glue->prev_tevent_trace_data = NULL;
784 TALLOC_FREE(glue->fd_map);
785 glue->num_maps = 0;
787 TALLOC_FREE(glue->gpollfds);
788 glue->num_gpollfds = 0;
790 TALLOC_FREE(glue->prev_gpollfds);
791 glue->num_prev_gpollfds = 0;
793 TALLOC_FREE(glue->timer);
794 TALLOC_FREE(glue->acquire_retry_timer);
795 TALLOC_FREE(glue->im);
798 * These are not really needed, but let's wipe the slate clean.
800 glue->skip_glib_refresh = false;
801 glue->gtimeout = 0;
802 glue->gpriority = 0;
805 static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
807 tevent_glib_glue_cleanup(glue);
809 glue->im = tevent_create_immediate(glue);
810 if (glue->im == NULL) {
811 return false;
814 tevent_get_trace_callback(glue->ev,
815 &glue->prev_tevent_trace_cb,
816 &glue->prev_tevent_trace_data);
817 tevent_set_trace_callback(glue->ev,
818 tevent_glib_glue_trace_callback,
819 glue);
821 return true;
824 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
826 tevent_glib_glue_cleanup(glue);
827 glue->quit = true;
828 return;
831 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
832 struct tevent_context *ev,
833 GMainContext *gmain_ctx)
835 bool ok;
836 struct tevent_glib_glue *glue = NULL;
838 glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
839 if (glue == NULL) {
840 DBG_ERR("talloc_zero failed\n");
841 return NULL;
844 *glue = (struct tevent_glib_glue) {
845 .ev = ev,
846 .gmain_ctx = gmain_ctx,
849 glue->im = tevent_create_immediate(glue);
851 tevent_get_trace_callback(glue->ev,
852 &glue->prev_tevent_trace_cb,
853 &glue->prev_tevent_trace_data);
854 tevent_set_trace_callback(glue->ev,
855 tevent_glib_glue_trace_callback,
856 glue);
858 ok = tevent_glib_prepare(glue);
859 if (!ok) {
860 TALLOC_FREE(glue);
861 return NULL;
864 return glue;
867 #else /* HAVE_GLIB */
869 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
870 struct tevent_context *ev,
871 GMainContext *gmain_ctx)
873 errno = ENOSYS;
874 return NULL;
877 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
879 return;
881 #endif /* HAVE_GLIB */