Line data Source code
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 393363 : 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 393363 : bool ok;
151 393363 : size_t i = 0, j = 0;
152 393363 : int cmp;
153 :
154 1573285 : while (i < num_gfds && j < num_prev_gfds) {
155 1179922 : cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
156 1179922 : if (cmp == 0) {
157 1179921 : ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
158 1179921 : if (!ok) {
159 : return false;
160 : }
161 1179921 : i++;
162 1179921 : j++;
163 1 : } else if (cmp < 0) {
164 1 : ok = new_cb(glue, &gfds[i]);
165 1 : if (!ok) {
166 : return false;
167 : }
168 1 : i++;
169 : } else {
170 0 : ok = removed_cb(glue, &prev_gfds[j]);
171 0 : if (!ok) {
172 : return false;
173 : }
174 0 : j++;
175 : }
176 : }
177 :
178 393368 : while (i < num_gfds) {
179 5 : ok = new_cb(glue, &gfds[i++]);
180 5 : if (!ok) {
181 : return false;
182 : }
183 : }
184 :
185 393364 : while (j < num_prev_gfds) {
186 1 : ok = removed_cb(glue, &prev_gfds[j++]);
187 1 : if (!ok) {
188 : return false;
189 : }
190 : }
191 :
192 : return true;
193 : }
194 :
195 1966486 : static int glib_fd_cmp_func(const void *p1, const void *p2)
196 : {
197 1966486 : const GPollFD *lhs = p1;
198 1966486 : const GPollFD *rhs = p2;
199 :
200 1966486 : if (lhs->fd < rhs->fd) {
201 : return -1;
202 1179921 : } else if (lhs->fd > rhs->fd) {
203 0 : 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 1179921 : static bool match_gfd_cb(struct tevent_glib_glue *glue,
214 : const GPollFD *new_gfd,
215 : const GPollFD *old_gfd)
216 : {
217 1179921 : size_t i;
218 1179921 : struct fd_map *fd_map = NULL;
219 1179921 : struct tevent_fd *fd_event = NULL;
220 :
221 1179921 : if (new_gfd->events == old_gfd->events) {
222 : return true;
223 : }
224 :
225 0 : for (i = 0; i < glue->num_maps; i++) {
226 0 : if (glue->fd_map[i]->fd == new_gfd->fd) {
227 : break;
228 : }
229 : }
230 :
231 0 : if (i == glue->num_maps) {
232 0 : DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
233 0 : return false;
234 : }
235 :
236 0 : fd_map = glue->fd_map[i];
237 0 : if (fd_map == NULL) {
238 0 : DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd);
239 0 : return false;
240 : }
241 :
242 0 : fd_event = fd_map->fd_event;
243 0 : if (fd_event == NULL) {
244 0 : DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd);
245 0 : return false;
246 : }
247 :
248 0 : tevent_fd_set_flags(fd_event, 0);
249 :
250 0 : if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
251 0 : TEVENT_FD_READABLE(fd_event);
252 : }
253 0 : if (new_gfd->events & G_IO_OUT) {
254 0 : TEVENT_FD_WRITEABLE(fd_event);
255 : }
256 :
257 : return true;
258 : }
259 :
260 6 : static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
261 : {
262 6 : struct tevent_fd *fd_event = NULL;
263 6 : struct fd_map *fd_map = NULL;
264 6 : uint16_t events = 0;
265 6 : bool revent;
266 6 : bool wevent;
267 :
268 6 : revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR));
269 6 : wevent = (gfd->events & G_IO_OUT);
270 6 : if (revent) {
271 5 : events |= TEVENT_FD_READ;
272 : }
273 6 : if (wevent) {
274 1 : events |= TEVENT_FD_WRITE;
275 : }
276 :
277 6 : glue->fd_map = talloc_realloc(glue,
278 : glue->fd_map,
279 : struct fd_map *,
280 : glue->num_maps + 1);
281 6 : if (glue->fd_map == NULL) {
282 0 : DBG_ERR("talloc_realloc failed\n");
283 0 : return false;
284 : }
285 6 : fd_map = talloc_zero(glue->fd_map, struct fd_map);
286 6 : if (fd_map == NULL) {
287 0 : DBG_ERR("talloc_realloc failed\n");
288 0 : return false;
289 : }
290 6 : glue->fd_map[glue->num_maps] = fd_map;
291 6 : glue->num_maps++;
292 :
293 6 : 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 6 : if (fd_event == NULL) {
300 0 : DBG_ERR("tevent_add_fd failed\n");
301 0 : return false;
302 : }
303 :
304 6 : *fd_map = (struct fd_map) {
305 : .glue = glue,
306 6 : .fd = gfd->fd,
307 : .fd_event = fd_event,
308 : };
309 :
310 6 : DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
311 :
312 : return true;
313 : }
314 :
315 1 : static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
316 : {
317 1 : size_t i;
318 :
319 2 : for (i = 0; i < glue->num_maps; i++) {
320 2 : if (glue->fd_map[i]->fd == gfd->fd) {
321 : break;
322 : }
323 : }
324 :
325 1 : if (i == glue->num_maps) {
326 0 : DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
327 0 : return false;
328 : }
329 :
330 1 : TALLOC_FREE(glue->fd_map[i]->fd_event);
331 1 : TALLOC_FREE(glue->fd_map[i]);
332 :
333 1 : if (i + 1 < glue->num_maps) {
334 2 : memmove(&glue->fd_map[i],
335 1 : &glue->fd_map[i+1],
336 1 : (glue->num_maps - (i + 1)) * sizeof(struct fd_map *));
337 : }
338 :
339 1 : glue->fd_map = talloc_realloc(glue,
340 : glue->fd_map,
341 : struct fd_map *,
342 : glue->num_maps - 1);
343 1 : if (glue->num_maps > 0 && glue->fd_map == NULL) {
344 0 : DBG_ERR("talloc_realloc failed\n");
345 0 : return false;
346 : }
347 1 : glue->num_maps--;
348 :
349 1 : return true;
350 : }
351 :
352 393342 : static short gpoll_to_poll_event(gushort gevent)
353 : {
354 393342 : short pevent = 0;
355 :
356 393342 : if (gevent & G_IO_IN) {
357 262269 : pevent |= POLLIN;
358 : }
359 393342 : if (gevent & G_IO_OUT) {
360 131073 : pevent |= POLLOUT;
361 : }
362 393342 : if (gevent & G_IO_HUP) {
363 0 : pevent |= POLLHUP;
364 : }
365 393342 : if (gevent & G_IO_ERR) {
366 0 : pevent |= POLLERR;
367 : }
368 :
369 393342 : return pevent;
370 : }
371 :
372 393342 : static gushort poll_to_gpoll_event(short pevent)
373 : {
374 393342 : gushort gevent = 0;
375 :
376 393342 : if (pevent & POLLIN) {
377 262269 : gevent |= G_IO_IN;
378 : }
379 393342 : if (pevent & POLLOUT) {
380 131073 : gevent |= G_IO_OUT;
381 : }
382 393342 : if (pevent & POLLHUP) {
383 0 : gevent |= G_IO_HUP;
384 : }
385 393342 : if (pevent & POLLERR) {
386 0 : gevent |= G_IO_ERR;
387 : }
388 :
389 393342 : return gevent;
390 : }
391 :
392 393342 : 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 393342 : struct fd_map *fd_map = talloc_get_type_abort(
398 : private_data, struct fd_map);
399 393342 : struct tevent_glib_glue *glue = NULL;
400 393342 : GPollFD *gpollfd = NULL;
401 393342 : struct pollfd fd;
402 393342 : int ret;
403 393342 : int i;
404 :
405 393342 : glue = fd_map->glue;
406 :
407 786621 : for (i = 0; i < glue->num_gpollfds; i++) {
408 786621 : if (glue->gpollfds[i].fd != fd_map->fd) {
409 393279 : continue;
410 : }
411 : gpollfd = &glue->gpollfds[i];
412 : break;
413 : }
414 393342 : if (gpollfd == NULL) {
415 0 : DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
416 : fd_map, fd_map->fd);
417 0 : 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 786684 : fd = (struct pollfd) {
426 393342 : .fd = gpollfd->fd,
427 393342 : .events = gpoll_to_poll_event(gpollfd->events),
428 : };
429 :
430 393342 : ret = sys_poll_intr(&fd, 1, 0);
431 393342 : if (ret == -1) {
432 0 : DBG_ERR("poll: %s\n", strerror(errno));
433 0 : return;
434 : }
435 393342 : if (ret == 0) {
436 : return;
437 : }
438 :
439 393342 : gpollfd->revents = poll_to_gpoll_event(fd.revents);
440 :
441 393342 : tevent_glib_process(glue);
442 393342 : return;
443 : }
444 :
445 19 : 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 19 : struct tevent_glib_glue *glue = talloc_get_type_abort(
451 : private_data, struct tevent_glib_glue);
452 :
453 19 : glue->timer = NULL;
454 19 : tevent_glib_process(glue);
455 19 : return;
456 : }
457 :
458 0 : static void tevent_glib_im_handler(struct tevent_context *ev,
459 : struct tevent_immediate *im,
460 : void *private_data)
461 : {
462 0 : struct tevent_glib_glue *glue = talloc_get_type_abort(
463 : private_data, struct tevent_glib_glue);
464 :
465 0 : glue->scheduled_im = false;
466 0 : tevent_glib_process(glue);
467 0 : return;
468 : }
469 :
470 393363 : 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 393363 : if (glue->num_prev_gpollfds < glue->num_gpollfds) {
477 6 : glue->prev_gpollfds = talloc_realloc(glue,
478 : glue->prev_gpollfds,
479 : GPollFD,
480 : glue->num_gpollfds);
481 6 : if (glue->prev_gpollfds == NULL) {
482 0 : DBG_ERR("talloc_realloc failed\n");
483 0 : return false;
484 : }
485 : }
486 393363 : glue->num_prev_gpollfds = glue->num_gpollfds;
487 393363 : if (glue->num_gpollfds > 0) {
488 393359 : memcpy(glue->prev_gpollfds, glue->gpollfds,
489 393359 : sizeof(GPollFD) * glue->num_gpollfds);
490 393359 : memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
491 : }
492 :
493 : return true;
494 : }
495 :
496 393363 : static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
497 : {
498 393363 : bool ok;
499 393363 : gint num_fds;
500 :
501 393363 : ok = save_current_fdset(glue);
502 393363 : if (!ok) {
503 : return false;
504 : }
505 :
506 393377 : while (true) {
507 393370 : num_fds = g_main_context_query(glue->gmain_ctx,
508 : glue->gpriority,
509 : &glue->gtimeout,
510 : glue->gpollfds,
511 : glue->num_gpollfds);
512 393370 : if (num_fds == glue->num_gpollfds) {
513 : break;
514 : }
515 7 : glue->gpollfds = talloc_realloc(glue,
516 : glue->gpollfds,
517 : GPollFD,
518 : num_fds);
519 7 : if (num_fds > 0 && glue->gpollfds == NULL) {
520 0 : DBG_ERR("talloc_realloc failed\n");
521 0 : return false;
522 : }
523 7 : glue->num_gpollfds = num_fds;
524 393363 : };
525 :
526 393363 : if (glue->num_gpollfds > 0) {
527 393363 : qsort(glue->gpollfds,
528 : num_fds,
529 : sizeof(GPollFD),
530 : glib_fd_cmp_func);
531 : }
532 :
533 393363 : DBG_DEBUG("num fds: %d, timeout: %d ms\n",
534 : num_fds, glue->gtimeout);
535 :
536 : return true;
537 : }
538 :
539 393363 : static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
540 : {
541 393363 : struct timeval tv;
542 393363 : bool ok;
543 :
544 786726 : ok = cmp_gfds(glue,
545 : glue->gpollfds,
546 : glue->prev_gpollfds,
547 393363 : glue->num_gpollfds,
548 393363 : glue->num_prev_gpollfds,
549 : glib_fd_cmp_func,
550 : match_gfd_cb,
551 : new_gfd_cb,
552 : remove_gfd_cb);
553 393363 : if (!ok) {
554 : return false;
555 : }
556 :
557 393363 : TALLOC_FREE(glue->timer);
558 :
559 393363 : if (glue->gtimeout == -1) {
560 : return true;
561 : }
562 :
563 19 : 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 0 : if (glue->scheduled_im) {
572 : return true;
573 : }
574 0 : tevent_schedule_immediate(glue->im,
575 : glue->ev,
576 : tevent_glib_im_handler,
577 0 : glue);
578 0 : glue->scheduled_im = true;
579 0 : return true;
580 : }
581 :
582 19 : tv = tevent_timeval_current_ofs(glue->gtimeout / 1000,
583 19 : (glue->gtimeout % 1000) * 1000);
584 :
585 19 : glue->timer = tevent_add_timer(glue->ev,
586 : glue,
587 : tv,
588 : tevent_glib_timer_handler,
589 : glue);
590 19 : if (glue->timer == NULL) {
591 0 : DBG_ERR("tevent_add_timer failed\n");
592 0 : return false;
593 : }
594 :
595 : return true;
596 : }
597 :
598 0 : 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 0 : struct tevent_glib_glue *glue = talloc_get_type_abort(
604 : private_data, struct tevent_glib_glue);
605 :
606 0 : glue->acquire_retry_timer = NULL;
607 0 : (void)tevent_glib_prepare(glue);
608 0 : }
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 393366 : static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
624 : {
625 393366 : bool ok;
626 393366 : gboolean gok;
627 :
628 393366 : if (glue->quit) {
629 : /* Set via samba_tevent_glib_glue_quit() */
630 : return true;
631 : }
632 :
633 393363 : 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 393363 : if (glue->gmain_owner) {
642 393359 : g_main_context_release(glue->gmain_ctx);
643 393359 : glue->gmain_owner = false;
644 : }
645 :
646 393363 : gok = g_main_context_acquire(glue->gmain_ctx);
647 393363 : if (!gok) {
648 0 : 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 0 : ok = tevent_glib_glue_reinit(glue);
657 0 : if (!ok) {
658 0 : DBG_ERR("tevent_glib_glue_reinit failed\n");
659 0 : samba_tevent_glib_glue_quit(glue);
660 0 : return false;
661 : }
662 :
663 0 : 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 0 : if (glue->acquire_retry_timer == NULL) {
670 0 : DBG_ERR("tevent_add_timer failed\n");
671 0 : samba_tevent_glib_glue_quit(glue);
672 0 : return false;
673 : }
674 : return true;
675 : }
676 393363 : 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 393363 : (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
683 :
684 393363 : ok = get_glib_fds_and_timeout(glue);
685 393363 : if (!ok) {
686 0 : DBG_ERR("get_glib_fds_and_timeout failed\n");
687 0 : samba_tevent_glib_glue_quit(glue);
688 0 : return false;
689 : }
690 :
691 393363 : ok = tevent_glib_update_events(glue);
692 393363 : if (!ok) {
693 0 : DBG_ERR("tevent_glib_update_events failed\n");
694 0 : samba_tevent_glib_glue_quit(glue);
695 0 : 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 393361 : static bool tevent_glib_process(struct tevent_glib_glue *glue)
720 : {
721 393361 : bool ok;
722 :
723 393361 : 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 393361 : (void)g_main_context_check(glue->gmain_ctx,
735 : glue->gpriority,
736 : glue->gpollfds,
737 : glue->num_gpollfds);
738 :
739 393361 : g_main_context_dispatch(glue->gmain_ctx);
740 :
741 393361 : ok = tevent_glib_prepare(glue);
742 393361 : if (!ok) {
743 : return false;
744 : }
745 393361 : glue->skip_glib_refresh = true;
746 393361 : return true;
747 : }
748 :
749 1573445 : static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
750 : void *private_data)
751 : {
752 1573445 : struct tevent_glib_glue *glue = talloc_get_type_abort(
753 : private_data, struct tevent_glib_glue);
754 :
755 1573445 : if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
756 393359 : if (!glue->skip_glib_refresh) {
757 1 : tevent_glib_prepare(glue);
758 : }
759 393359 : glue->skip_glib_refresh = false;
760 : }
761 :
762 : /* chain previous handler */
763 1573445 : if (glue->prev_tevent_trace_cb != NULL) {
764 0 : glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
765 : }
766 1573445 : }
767 :
768 5 : static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
769 : {
770 5 : size_t n = talloc_array_length(glue->fd_map);
771 5 : size_t i;
772 :
773 10 : for (i = 0; i < n; i++) {
774 5 : TALLOC_FREE(glue->fd_map[i]->fd_event);
775 5 : TALLOC_FREE(glue->fd_map[i]);
776 : }
777 :
778 5 : tevent_set_trace_callback(glue->ev,
779 : glue->prev_tevent_trace_cb,
780 : glue->prev_tevent_trace_data);
781 5 : glue->prev_tevent_trace_cb = NULL;
782 5 : glue->prev_tevent_trace_data = NULL;
783 :
784 5 : TALLOC_FREE(glue->fd_map);
785 5 : glue->num_maps = 0;
786 :
787 5 : TALLOC_FREE(glue->gpollfds);
788 5 : glue->num_gpollfds = 0;
789 :
790 5 : TALLOC_FREE(glue->prev_gpollfds);
791 5 : glue->num_prev_gpollfds = 0;
792 :
793 5 : TALLOC_FREE(glue->timer);
794 5 : TALLOC_FREE(glue->acquire_retry_timer);
795 5 : TALLOC_FREE(glue->im);
796 :
797 : /*
798 : * These are not really needed, but let's wipe the slate clean.
799 : */
800 5 : glue->skip_glib_refresh = false;
801 5 : glue->gtimeout = 0;
802 5 : glue->gpriority = 0;
803 5 : }
804 :
805 0 : static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
806 : {
807 0 : tevent_glib_glue_cleanup(glue);
808 :
809 0 : glue->im = tevent_create_immediate(glue);
810 0 : if (glue->im == NULL) {
811 : return false;
812 : }
813 :
814 0 : tevent_get_trace_callback(glue->ev,
815 : &glue->prev_tevent_trace_cb,
816 0 : &glue->prev_tevent_trace_data);
817 0 : tevent_set_trace_callback(glue->ev,
818 : tevent_glib_glue_trace_callback,
819 : glue);
820 :
821 0 : return true;
822 : }
823 :
824 5 : void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
825 : {
826 5 : tevent_glib_glue_cleanup(glue);
827 5 : glue->quit = true;
828 5 : return;
829 : }
830 :
831 4 : struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
832 : struct tevent_context *ev,
833 : GMainContext *gmain_ctx)
834 : {
835 4 : bool ok;
836 4 : struct tevent_glib_glue *glue = NULL;
837 :
838 4 : glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
839 4 : if (glue == NULL) {
840 0 : DBG_ERR("talloc_zero failed\n");
841 0 : return NULL;
842 : }
843 :
844 4 : *glue = (struct tevent_glib_glue) {
845 : .ev = ev,
846 : .gmain_ctx = gmain_ctx,
847 : };
848 :
849 4 : glue->im = tevent_create_immediate(glue);
850 :
851 4 : tevent_get_trace_callback(glue->ev,
852 : &glue->prev_tevent_trace_cb,
853 4 : &glue->prev_tevent_trace_data);
854 4 : tevent_set_trace_callback(glue->ev,
855 : tevent_glib_glue_trace_callback,
856 : glue);
857 :
858 4 : ok = tevent_glib_prepare(glue);
859 4 : if (!ok) {
860 0 : TALLOC_FREE(glue);
861 0 : 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 */
|