s3:eventlog: Fix code spelling
[samba.git] / source3 / lib / tevent_glib_glue.c
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
6
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
10
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.
15
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.
20
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/>.
23 */
24
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>
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_TEVENT
33
34 #ifdef HAVE_GLIB
35 #include <glib.h>
36 #include "tevent_glib_glue.h"
37
38 struct fd_map {
39         struct tevent_glib_glue *glue;
40         int fd;
41         struct tevent_fd *fd_event;
42 };
43
44 struct tevent_glib_glue {
45         /*
46          * The tevent context we're feeding.
47          */
48         struct tevent_context *ev;
49
50         /*
51          * The glib gmain context we're polling and whether we're currently
52          * owning it by virtue of g_main_context_acquire().
53          */
54         GMainContext *gmain_ctx;
55         bool gmain_owner;
56
57         /*
58          * Set by samba_tevent_glib_glue_quit().
59          */
60         bool quit;
61
62         /*
63          * tevent trace callback and data we got from tevent_get_trace_callback()
64          * before installing our own trace callback.
65          */
66         tevent_trace_callback_t prev_tevent_trace_cb;
67         void *prev_tevent_trace_data;
68
69         /*
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.
73          */
74         bool skip_glib_refresh;
75
76         /*
77          * Used when acquiring the glib gmain context failed.
78          */
79         struct tevent_timer *acquire_retry_timer;
80
81         /*
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.
87          */
88         gint gtimeout;
89         gint gpriority;
90         struct tevent_timer *timer;
91         struct tevent_immediate *im;
92         bool scheduled_im;
93
94         /*
95          * glib gmain context fds returned from g_main_context_query(). These
96          * get translated to tevent fd events.
97          */
98         GPollFD *gpollfds;
99         gint num_gpollfds;
100
101         /*
102          * A copy of gpollfds and num_gpollfds from the previous event loop
103          * iteration, used to detect changes in the set of fds.
104          */
105         GPollFD *prev_gpollfds;
106         gint num_prev_gpollfds;
107
108         /*
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.
111          */
112         struct fd_map **fd_map;
113         size_t num_maps;
114 };
115
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);
123
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);
132
133 /**
134  * Compare two sorted GPollFD arrays
135  *
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.
139  **/
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)
149 {
150         bool ok;
151         size_t i = 0, j = 0;
152         int cmp;
153
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;
160                         }
161                         i++;
162                         j++;
163                 } else if (cmp < 0) {
164                         ok = new_cb(glue, &gfds[i]);
165                         if (!ok) {
166                                 return false;
167                         }
168                         i++;
169                 } else {
170                         ok = removed_cb(glue, &prev_gfds[j]);
171                         if (!ok) {
172                                 return false;
173                         }
174                         j++;
175                 }
176         }
177
178         while (i < num_gfds) {
179                 ok = new_cb(glue, &gfds[i++]);
180                 if (!ok) {
181                         return false;
182                 }
183         }
184
185         while (j < num_prev_gfds) {
186                 ok = removed_cb(glue, &prev_gfds[j++]);
187                 if (!ok) {
188                         return false;
189                 }
190         }
191
192         return true;
193 }
194
195 static int glib_fd_cmp_func(const void *p1, const void *p2)
196 {
197         const GPollFD *lhs = p1;
198         const GPollFD *rhs = p2;
199
200         if (lhs->fd < rhs->fd) {
201                 return -1;
202         } else if (lhs->fd > rhs->fd) {
203                 return 1;
204         }
205
206         return 0;
207 }
208
209 /*
210  * We already have a tevent fd event for the glib GPollFD, but we may have to
211  * update flags.
212  */
213 static bool match_gfd_cb(struct tevent_glib_glue *glue,
214                          const GPollFD *new_gfd,
215                          const GPollFD *old_gfd)
216 {
217         size_t i;
218         struct fd_map *fd_map = NULL;
219         struct tevent_fd *fd_event = NULL;
220
221         if (new_gfd->events == old_gfd->events) {
222                 return true;
223         }
224
225         for (i = 0; i < glue->num_maps; i++) {
226                 if (glue->fd_map[i]->fd == new_gfd->fd) {
227                         break;
228                 }
229         }
230
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;
234         }
235
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;
240         }
241
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;
246         }
247
248         tevent_fd_set_flags(fd_event, 0);
249
250         if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
251                 TEVENT_FD_READABLE(fd_event);
252         }
253         if (new_gfd->events & G_IO_OUT) {
254                 TEVENT_FD_WRITEABLE(fd_event);
255         }
256
257         return true;
258 }
259
260 static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
261 {
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;
267
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;
272         }
273         if (wevent) {
274                 events |= TEVENT_FD_WRITE;
275         }
276
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;
284         }
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;
289         }
290         glue->fd_map[glue->num_maps] = fd_map;
291         glue->num_maps++;
292
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;
302         }
303
304         *fd_map = (struct fd_map) {
305                 .glue = glue,
306                 .fd = gfd->fd,
307                 .fd_event = fd_event,
308         };
309
310         DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
311
312         return true;
313 }
314
315 static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
316 {
317         size_t i;
318
319         for (i = 0; i < glue->num_maps; i++) {
320                 if (glue->fd_map[i]->fd == gfd->fd) {
321                         break;
322                 }
323         }
324
325         if (i == glue->num_maps) {
326                 DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
327                 return false;
328         }
329
330         TALLOC_FREE(glue->fd_map[i]->fd_event);
331         TALLOC_FREE(glue->fd_map[i]);
332
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 *));
337         }
338
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;
346         }
347         glue->num_maps--;
348
349         return true;
350 }
351
352 static short gpoll_to_poll_event(gushort gevent)
353 {
354         short pevent = 0;
355
356         if (gevent & G_IO_IN) {
357                 pevent |= POLLIN;
358         }
359         if (gevent & G_IO_OUT) {
360                 pevent |= POLLOUT;
361         }
362         if (gevent & G_IO_HUP) {
363                 pevent |= POLLHUP;
364         }
365         if (gevent & G_IO_ERR) {
366                 pevent |= POLLERR;
367         }
368
369         return pevent;
370 }
371
372 static gushort poll_to_gpoll_event(short pevent)
373 {
374         gushort gevent = 0;
375
376         if (pevent & POLLIN) {
377                 gevent |= G_IO_IN;
378         }
379         if (pevent & POLLOUT) {
380                 gevent |= G_IO_OUT;
381         }
382         if (pevent & POLLHUP) {
383                 gevent |= G_IO_HUP;
384         }
385         if (pevent & POLLERR) {
386                 gevent |= G_IO_ERR;
387         }
388
389         return gevent;
390 }
391
392 static void tevent_glib_fd_handler(struct tevent_context *ev,
393                                    struct tevent_fd *fde,
394                                    uint16_t flags,
395                                    void *private_data)
396 {
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;
404
405         glue = fd_map->glue;
406
407         for (i = 0; i < glue->num_gpollfds; i++) {
408                 if (glue->gpollfds[i].fd != fd_map->fd) {
409                         continue;
410                 }
411                 gpollfd = &glue->gpollfds[i];
412                 break;
413         }
414         if (gpollfd == NULL) {
415                 DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
416                         fd_map, fd_map->fd);
417                 return;
418         }
419         /*
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.
423          */
424
425         fd = (struct pollfd) {
426                 .fd = gpollfd->fd,
427                 .events = gpoll_to_poll_event(gpollfd->events),
428         };
429
430         ret = sys_poll_intr(&fd, 1, 0);
431         if (ret == -1) {
432                 DBG_ERR("poll: %s\n", strerror(errno));
433                 return;
434         }
435         if (ret == 0) {
436                 return;
437         }
438
439         gpollfd->revents = poll_to_gpoll_event(fd.revents);
440
441         tevent_glib_process(glue);
442         return;
443 }
444
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)
449 {
450         struct tevent_glib_glue *glue = talloc_get_type_abort(
451                 private_data, struct tevent_glib_glue);
452
453         glue->timer = NULL;
454         tevent_glib_process(glue);
455         return;
456 }
457
458 static void tevent_glib_im_handler(struct tevent_context *ev,
459                                    struct tevent_immediate *im,
460                                    void *private_data)
461 {
462         struct tevent_glib_glue *glue = talloc_get_type_abort(
463                 private_data, struct tevent_glib_glue);
464
465         glue->scheduled_im = false;
466         tevent_glib_process(glue);
467         return;
468 }
469
470 static bool save_current_fdset(struct tevent_glib_glue *glue)
471 {
472         /*
473          * Save old glib fds. We only grow the prev array.
474          */
475
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;
484                 }
485         }
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);
491         }
492
493         return true;
494 }
495
496 static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
497 {
498         bool ok;
499         gint num_fds;
500
501         ok = save_current_fdset(glue);
502         if (!ok) {
503                 return false;
504         }
505
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;
514                 }
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;
522                 }
523                 glue->num_gpollfds = num_fds;
524         };
525
526         if (glue->num_gpollfds > 0) {
527                 qsort(glue->gpollfds,
528                       num_fds,
529                       sizeof(GPollFD),
530                       glib_fd_cmp_func);
531         }
532
533         DBG_DEBUG("num fds: %d, timeout: %d ms\n",
534                   num_fds, glue->gtimeout);
535
536         return true;
537 }
538
539 static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
540 {
541         struct timeval tv;
542         bool ok;
543
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;
555         }
556
557         TALLOC_FREE(glue->timer);
558
559         if (glue->gtimeout == -1) {
560                 return true;
561         }
562
563         if (glue->gtimeout == 0) {
564                 /*
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.
570                  */
571                 if (glue->scheduled_im) {
572                         return true;
573                 }
574                 tevent_schedule_immediate(glue->im,
575                                           glue->ev,
576                                           tevent_glib_im_handler,
577                                           glue);
578                 glue->scheduled_im = true;
579                 return true;
580         }
581
582         tv = tevent_timeval_current_ofs(glue->gtimeout / 1000,
583                                         (glue->gtimeout % 1000) * 1000);
584
585         glue->timer = tevent_add_timer(glue->ev,
586                                        glue,
587                                        tv,
588                                        tevent_glib_timer_handler,
589                                        glue);
590         if (glue->timer == NULL) {
591                 DBG_ERR("tevent_add_timer failed\n");
592                 return false;
593         }
594
595         return true;
596 }
597
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)
602 {
603         struct tevent_glib_glue *glue = talloc_get_type_abort(
604                 private_data, struct tevent_glib_glue);
605
606         glue->acquire_retry_timer = NULL;
607         (void)tevent_glib_prepare(glue);
608 }
609
610 /**
611  * Fetch glib event sources and add them to tevent
612  *
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.
617  *
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.
622  **/
623 static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
624 {
625         bool ok;
626         gboolean gok;
627
628         if (glue->quit) {
629                 /* Set via samba_tevent_glib_glue_quit() */
630                 return true;
631         }
632
633         if (glue->acquire_retry_timer != NULL) {
634                 /*
635                  * We're still waiting on the below g_main_context_acquire() to
636                  * succeed, just return.
637                  */
638                 return true;
639         }
640
641         if (glue->gmain_owner) {
642                 g_main_context_release(glue->gmain_ctx);
643                 glue->gmain_owner = false;
644         }
645
646         gok = g_main_context_acquire(glue->gmain_ctx);
647         if (!gok) {
648                 DBG_ERR("couldn't acquire g_main_context\n");
649
650                 /*
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.
655                  */
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;
661                 }
662
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;
673                 }
674                 return true;
675         }
676         glue->gmain_owner = true;
677
678         /*
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.
681          */
682         (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
683
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;
689         }
690
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;
696         }
697
698         return true;
699 }
700
701 /**
702  * Process pending glib events
703  *
704  * tevent_glib_process() gets called to process pending glib events via
705  * g_main_context_check() and then g_main_context_dispatch().
706  *
707  * After pending event handlers are dispatched, we rearm the glib glue event
708  * handlers in tevent by calling tevent_glib_prepare().
709  *
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.
713  *
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.
718  */
719 static bool tevent_glib_process(struct tevent_glib_glue *glue)
720 {
721         bool ok;
722
723         DBG_DEBUG("tevent_glib_process\n");
724
725         /*
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.
731          *
732          * https://bugzilla.gnome.org/show_bug.cgi?id=11059
733          */
734         (void)g_main_context_check(glue->gmain_ctx,
735                                    glue->gpriority,
736                                    glue->gpollfds,
737                                    glue->num_gpollfds);
738
739         g_main_context_dispatch(glue->gmain_ctx);
740
741         ok = tevent_glib_prepare(glue);
742         if (!ok) {
743                 return false;
744         }
745         glue->skip_glib_refresh = true;
746         return true;
747 }
748
749 static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
750                                             void *private_data)
751 {
752         struct tevent_glib_glue *glue = talloc_get_type_abort(
753                 private_data, struct tevent_glib_glue);
754
755         if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
756                 if (!glue->skip_glib_refresh) {
757                         tevent_glib_prepare(glue);
758                 }
759                 glue->skip_glib_refresh = false;
760         }
761
762         /* chain previous handler */
763         if (glue->prev_tevent_trace_cb != NULL) {
764                 glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
765         }
766 }
767
768 static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
769 {
770         size_t n = talloc_array_length(glue->fd_map);
771         size_t i;
772
773         for (i = 0; i < n; i++) {
774                 TALLOC_FREE(glue->fd_map[i]->fd_event);
775                 TALLOC_FREE(glue->fd_map[i]);
776         }
777
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;
783
784         TALLOC_FREE(glue->fd_map);
785         glue->num_maps = 0;
786
787         TALLOC_FREE(glue->gpollfds);
788         glue->num_gpollfds = 0;
789
790         TALLOC_FREE(glue->prev_gpollfds);
791         glue->num_prev_gpollfds = 0;
792
793         TALLOC_FREE(glue->timer);
794         TALLOC_FREE(glue->acquire_retry_timer);
795         TALLOC_FREE(glue->im);
796
797         /*
798          * These are not really needed, but let's wipe the slate clean.
799          */
800         glue->skip_glib_refresh = false;
801         glue->gtimeout = 0;
802         glue->gpriority = 0;
803 }
804
805 static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
806 {
807         tevent_glib_glue_cleanup(glue);
808
809         glue->im = tevent_create_immediate(glue);
810         if (glue->im == NULL) {
811                 return false;
812         }
813
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);
820
821         return true;
822 }
823
824 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
825 {
826         tevent_glib_glue_cleanup(glue);
827         glue->quit = true;
828         return;
829 }
830
831 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
832                                                        struct tevent_context *ev,
833                                                        GMainContext *gmain_ctx)
834 {
835         bool ok;
836         struct tevent_glib_glue *glue = NULL;
837
838         glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
839         if (glue == NULL) {
840                 DBG_ERR("talloc_zero failed\n");
841                 return NULL;
842         }
843
844         *glue = (struct tevent_glib_glue) {
845                 .ev = ev,
846                 .gmain_ctx = gmain_ctx,
847         };
848
849         glue->im = tevent_create_immediate(glue);
850
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);
857
858         ok = tevent_glib_prepare(glue);
859         if (!ok) {
860                 TALLOC_FREE(glue);
861                 return NULL;
862         }
863
864         return glue;
865 }
866
867 #else /* HAVE_GLIB */
868
869 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
870                                                        struct tevent_context *ev,
871                                                        GMainContext *gmain_ctx)
872 {
873         errno = ENOSYS;
874         return NULL;
875 }
876
877 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
878 {
879         return;
880 }
881 #endif /* HAVE_GLIB */