]> git.samba.org - samba.git/blob - lib/poll_funcs/poll_funcs_tevent.c
lib: Remove unix_msg
[samba.git] / lib / poll_funcs / poll_funcs_tevent.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Copyright (C) Volker Lendecke 2013,2014
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "poll_funcs_tevent.h"
20 #include "tevent.h"
21 #include "system/select.h"
22 #include "lib/util/dlinklist.h"
23
24 /*
25  * A poll_watch is asked for by the engine using this library via
26  * funcs->watch_new(). It represents interest in "fd" becoming readable or
27  * writable.
28  */
29
30 struct poll_watch {
31         struct poll_funcs_state *state;
32         size_t slot;            /* index into state->watches[] */
33         int fd;
34         int events;
35         void (*callback)(struct poll_watch *w, int fd, short events,
36                          void *private_data);
37         void *private_data;
38 };
39
40 struct poll_timeout {
41         struct poll_funcs_state *state;
42         size_t slot;            /* index into state->timeouts[] */
43         struct timeval tv;
44         void (*callback)(struct poll_timeout *t, void *private_data);
45         void *private_data;
46 };
47
48 struct poll_funcs_state {
49         /*
50          * "watches" is the array of all watches that we have handed out via
51          * funcs->watch_new(). The "watches" array can contain NULL pointers.
52          */
53         struct poll_watch **watches;
54
55         /*
56          * Like "watches" for timeouts;
57          */
58         struct poll_timeout **timeouts;
59
60         /*
61          * "contexts is the array of tevent_contexts that serve
62          * "watches". "contexts" can contain NULL pointers.
63          */
64         struct poll_funcs_tevent_context **contexts;
65 };
66
67 struct poll_funcs_tevent_context {
68         struct poll_funcs_tevent_handle *handles;
69         struct poll_funcs_state *state;
70         unsigned slot;          /* index into state->contexts[] */
71         struct tevent_context *ev;
72         struct tevent_fd **fdes; /* same indexes as state->watches[] */
73         struct tevent_timer **timers;  /* same indexes as state->timeouts[] */
74 };
75
76 /*
77  * poll_funcs_tevent_register() hands out a struct poll_funcs_tevent_handle as
78  * a void *. poll_funcs_tevent_register allows tevent_contexts to be
79  * registered multiple times, and we can't add a tevent_fd for the same fd's
80  * multiple times. So we have to share one poll_funcs_tevent_context.
81  */
82 struct poll_funcs_tevent_handle {
83         struct poll_funcs_tevent_handle *prev, *next;
84         struct poll_funcs_tevent_context *ctx;
85 };
86
87 static uint16_t poll_events_to_tevent(short events)
88 {
89         uint16_t ret = 0;
90
91         if (events & POLLIN) {
92                 ret |= TEVENT_FD_READ;
93         }
94         if (events & POLLOUT) {
95                 ret |= TEVENT_FD_WRITE;
96         }
97         return ret;
98 }
99
100 static short tevent_to_poll_events(uint16_t flags)
101 {
102         short ret = 0;
103
104         if (flags & TEVENT_FD_READ) {
105                 ret |= POLLIN;
106         }
107         if (flags & TEVENT_FD_WRITE) {
108                 ret |= POLLOUT;
109         }
110         return ret;
111 }
112
113 /*
114  * Find or create a free slot in state->watches[]
115  */
116 static bool poll_funcs_watch_find_slot(struct poll_funcs_state *state,
117                                        size_t *slot)
118 {
119         struct poll_watch **watches;
120         size_t i, num_watches, num_contexts;
121
122         num_watches = talloc_array_length(state->watches);
123
124         for (i=0; i<num_watches; i++) {
125                 if (state->watches[i] == NULL) {
126                         *slot = i;
127                         return true;
128                 }
129         }
130
131         watches = talloc_realloc(state, state->watches, struct poll_watch *,
132                                  num_watches + 1);
133         if (watches == NULL) {
134                 return false;
135         }
136         watches[num_watches] = NULL;
137         state->watches = watches;
138
139         num_contexts = talloc_array_length(state->contexts);
140
141         for (i=0; i<num_contexts; i++) {
142                 struct tevent_fd **fdes;
143                 struct poll_funcs_tevent_context *c = state->contexts[i];
144                 if (c == NULL) {
145                         continue;
146                 }
147                 fdes = talloc_realloc(c, c->fdes, struct tevent_fd *,
148                                       num_watches + 1);
149                 if (fdes == NULL) {
150                         state->watches = talloc_realloc(
151                                 state, state->watches, struct poll_watch *,
152                                 num_watches);
153                         return false;
154                 }
155                 c->fdes = fdes;
156
157                 fdes[num_watches] = NULL;
158         }
159
160         *slot = num_watches;
161
162         return true;
163 }
164
165 static void poll_funcs_fde_handler(struct tevent_context *ev,
166                                    struct tevent_fd *fde, uint16_t flags,
167                                    void *private_data);
168 static int poll_watch_destructor(struct poll_watch *w);
169
170 static struct poll_watch *tevent_watch_new(
171         const struct poll_funcs *funcs, int fd, short events,
172         void (*callback)(struct poll_watch *w, int fd, short events,
173                          void *private_data),
174         void *private_data)
175 {
176         struct poll_funcs_state *state = talloc_get_type_abort(
177                 funcs->private_data, struct poll_funcs_state);
178         struct poll_watch *w;
179         size_t i, slot, num_contexts;
180
181         if (!poll_funcs_watch_find_slot(state, &slot)) {
182                 return NULL;
183         }
184
185         w = talloc(state->watches, struct poll_watch);
186         if (w == NULL) {
187                 return NULL;
188         }
189         w->state = state;
190         w->slot = slot;
191         w->fd = fd;
192         w->events = poll_events_to_tevent(events);
193         w->fd = fd;
194         w->callback = callback;
195         w->private_data = private_data;
196         state->watches[slot] = w;
197
198         talloc_set_destructor(w, poll_watch_destructor);
199
200         num_contexts = talloc_array_length(state->contexts);
201
202         for (i=0; i<num_contexts; i++) {
203                 struct poll_funcs_tevent_context *c = state->contexts[i];
204                 if (c == NULL) {
205                         continue;
206                 }
207                 c->fdes[slot] = tevent_add_fd(c->ev, c->fdes, w->fd, w->events,
208                                               poll_funcs_fde_handler, w);
209                 if (c->fdes[slot] == NULL) {
210                         goto fail;
211                 }
212         }
213         return w;
214
215 fail:
216         TALLOC_FREE(w);
217         return NULL;
218 }
219
220 static int poll_watch_destructor(struct poll_watch *w)
221 {
222         struct poll_funcs_state *state = w->state;
223         size_t num_contexts = talloc_array_length(state->contexts);
224         size_t slot = w->slot;
225         size_t i;
226
227         TALLOC_FREE(state->watches[slot]);
228
229         for (i=0; i<num_contexts; i++) {
230                 struct poll_funcs_tevent_context *c = state->contexts[i];
231                 if (c == NULL) {
232                         continue;
233                 }
234                 TALLOC_FREE(c->fdes[slot]);
235         }
236
237         return 0;
238 }
239
240 static void tevent_watch_update(struct poll_watch *w, short events)
241 {
242         struct poll_funcs_state *state = w->state;
243         size_t num_contexts = talloc_array_length(state->contexts);
244         size_t slot = w->slot;
245         size_t i;
246
247         w->events = poll_events_to_tevent(events);
248
249         for (i=0; i<num_contexts; i++) {
250                 struct poll_funcs_tevent_context *c = state->contexts[i];
251                 if (c == NULL) {
252                         continue;
253                 }
254                 tevent_fd_set_flags(c->fdes[slot], w->events);
255         }
256 }
257
258 static short tevent_watch_get_events(struct poll_watch *w)
259 {
260         return tevent_to_poll_events(w->events);
261 }
262
263 static void tevent_watch_free(struct poll_watch *w)
264 {
265         TALLOC_FREE(w);
266 }
267
268 static bool poll_funcs_timeout_find_slot(struct poll_funcs_state *state,
269                                          size_t *slot)
270 {
271         struct poll_timeout **timeouts;
272         size_t i, num_timeouts, num_contexts;
273
274         num_timeouts = talloc_array_length(state->timeouts);
275
276         for (i=0; i<num_timeouts; i++) {
277                 if (state->timeouts[i] == NULL) {
278                         *slot = i;
279                         return true;
280                 }
281         }
282
283         timeouts = talloc_realloc(state, state->timeouts,
284                                   struct poll_timeout *,
285                                   num_timeouts + 1);
286         if (timeouts == NULL) {
287                 return false;
288         }
289         timeouts[num_timeouts] = NULL;
290         state->timeouts = timeouts;
291
292         num_contexts = talloc_array_length(state->contexts);
293
294         for (i=0; i<num_contexts; i++) {
295                 struct tevent_timer **timers;
296                 struct poll_funcs_tevent_context *c = state->contexts[i];
297                 if (c == NULL) {
298                         continue;
299                 }
300                 timers = talloc_realloc(c, c->timers, struct tevent_timer *,
301                                         num_timeouts + 1);
302                 if (timers == NULL) {
303                         state->timeouts = talloc_realloc(
304                                 state, state->timeouts, struct poll_timeout *,
305                                 num_timeouts);
306                         return false;
307                 }
308                 c->timers = timers;
309
310                 timers[num_timeouts] = NULL;
311         }
312
313         *slot = num_timeouts;
314
315         return true;
316 }
317
318 static void poll_funcs_timer_handler(struct tevent_context *ev,
319                                      struct tevent_timer *te,
320                                      struct timeval current_time,
321                                      void *private_data);
322 static int poll_timeout_destructor(struct poll_timeout *t);
323
324 static struct poll_timeout *tevent_timeout_new(
325         const struct poll_funcs *funcs, const struct timeval tv,
326         void (*callback)(struct poll_timeout *t, void *private_data),
327         void *private_data)
328 {
329         struct poll_funcs_state *state = talloc_get_type_abort(
330                 funcs->private_data, struct poll_funcs_state);
331         struct poll_timeout *t;
332         size_t i, slot, num_contexts;
333
334         if (!poll_funcs_timeout_find_slot(state, &slot)) {
335                 return NULL;
336         }
337
338         t = talloc(state->timeouts, struct poll_timeout);
339         if (t == NULL) {
340                 return NULL;
341         }
342         t->state = state;
343         t->slot = slot;
344         t->tv = tv;
345         t->callback = callback;
346         t->private_data = private_data;
347
348         talloc_set_destructor(t, poll_timeout_destructor);
349
350         num_contexts = talloc_array_length(state->contexts);
351
352         for (i=0; i<num_contexts; i++) {
353                 struct poll_funcs_tevent_context *c = state->contexts[i];
354                 if (c == NULL) {
355                         continue;
356                 }
357                 c->timers[slot] = tevent_add_timer(
358                         c->ev, c->timers, tv, poll_funcs_timer_handler, t);
359                 if (c->timers[slot] == NULL) {
360                         goto fail;
361                 }
362         }
363         return t;
364
365 fail:
366         TALLOC_FREE(t);
367         return NULL;
368 }
369
370 static int poll_timeout_destructor(struct poll_timeout *t)
371 {
372         struct poll_funcs_state *state = t->state;
373         size_t num_contexts = talloc_array_length(state->contexts);
374         size_t slot = t->slot;
375         size_t i;
376
377         TALLOC_FREE(state->timeouts[slot]);
378
379         for (i=0; i<num_contexts; i++) {
380                 struct poll_funcs_tevent_context *c = state->contexts[i];
381                 if (c == NULL) {
382                         continue;
383                 }
384                 TALLOC_FREE(c->timers[slot]);
385         }
386
387         return 0;
388 }
389
390 static void poll_funcs_timer_handler(struct tevent_context *ev,
391                                      struct tevent_timer *te,
392                                      struct timeval current_time,
393                                      void *private_data)
394 {
395         struct poll_timeout *t = talloc_get_type_abort(
396                 private_data, struct poll_timeout);
397         struct poll_funcs_state *state = t->state;
398         size_t slot = t->slot;
399         size_t i, num_contexts;
400
401         num_contexts = talloc_array_length(state->contexts);
402
403         for (i=0; i<num_contexts; i++) {
404                 struct poll_funcs_tevent_context *c = state->contexts[i];
405                 if (c == NULL) {
406                         continue;
407                 }
408                 TALLOC_FREE(c->timers[slot]);
409         }
410
411         t->callback(t, t->private_data);
412 }
413
414 static void tevent_timeout_update(struct poll_timeout *t,
415                                   const struct timeval tv)
416 {
417         struct poll_funcs_state *state = t->state;
418         size_t num_contexts = talloc_array_length(state->contexts);
419         size_t slot = t->slot;
420         size_t i;
421
422         for (i=0; i<num_contexts; i++) {
423                 struct poll_funcs_tevent_context *c = state->contexts[i];
424                 if (c == NULL) {
425                         continue;
426                 }
427                 TALLOC_FREE(c->timers[slot]);
428
429                 c->timers[slot] = tevent_add_timer(
430                         c->ev, c->timers, tv, poll_funcs_timer_handler, t);
431                 if (c->timers[slot] == NULL) {
432                         /*
433                          * We just free'ed the space, why did this fail??
434                          */
435                         abort();
436                 }
437         }
438 }
439
440 static void tevent_timeout_free(struct poll_timeout *t)
441 {
442         TALLOC_FREE(t);
443 }
444
445 static int poll_funcs_state_destructor(struct poll_funcs_state *state);
446
447 struct poll_funcs *poll_funcs_init_tevent(TALLOC_CTX *mem_ctx)
448 {
449         struct poll_funcs *f;
450         struct poll_funcs_state *state;
451
452         f = talloc(mem_ctx, struct poll_funcs);
453         if (f == NULL) {
454                 return NULL;
455         }
456         state = talloc_zero(f, struct poll_funcs_state);
457         if (state == NULL) {
458                 TALLOC_FREE(f);
459                 return NULL;
460         }
461         talloc_set_destructor(state, poll_funcs_state_destructor);
462
463         f->watch_new = tevent_watch_new;
464         f->watch_update = tevent_watch_update;
465         f->watch_get_events = tevent_watch_get_events;
466         f->watch_free = tevent_watch_free;
467         f->timeout_new = tevent_timeout_new;
468         f->timeout_update = tevent_timeout_update;
469         f->timeout_free = tevent_timeout_free;
470         f->private_data = state;
471         return f;
472 }
473
474 static int poll_funcs_state_destructor(struct poll_funcs_state *state)
475 {
476         size_t num_watches = talloc_array_length(state->watches);
477         size_t num_timeouts = talloc_array_length(state->timeouts);
478         size_t num_contexts = talloc_array_length(state->contexts);
479         size_t i;
480         /*
481          * Make sure the watches are cleared before the contexts. The watches
482          * have destructors attached to them that clean up the fde's
483          */
484         for (i=0; i<num_watches; i++) {
485                 TALLOC_FREE(state->watches[i]);
486         }
487         for (i=0; i<num_timeouts; i++) {
488                 TALLOC_FREE(state->timeouts[i]);
489         }
490         for (i=0; i<num_contexts; i++) {
491                 TALLOC_FREE(state->contexts[i]);
492         }
493         return 0;
494 }
495
496 /*
497  * Find or create a free slot in state->contexts[]
498  */
499 static bool poll_funcs_context_slot_find(struct poll_funcs_state *state,
500                                          struct tevent_context *ev,
501                                          size_t *slot)
502 {
503         struct poll_funcs_tevent_context **contexts;
504         size_t num_contexts = talloc_array_length(state->contexts);
505         size_t i;
506
507         /* Look for an existing match first. */
508         for (i=0; i<num_contexts; i++) {
509                 struct poll_funcs_tevent_context *ctx = state->contexts[i];
510
511                 if (ctx != NULL && ctx->ev == ev) {
512                         *slot = i;
513                         return true;
514                 }
515         }
516
517         /* Now look for a free slot. */
518         for (i=0; i<num_contexts; i++) {
519                 struct poll_funcs_tevent_context *ctx = state->contexts[i];
520
521                 if (ctx == NULL) {
522                         *slot = i;
523                         return true;
524                 }
525         }
526
527         contexts = talloc_realloc(state, state->contexts,
528                                   struct poll_funcs_tevent_context *,
529                                   num_contexts + 1);
530         if (contexts == NULL) {
531                 return false;
532         }
533         state->contexts = contexts;
534         state->contexts[num_contexts] = NULL;
535
536         *slot = num_contexts;
537
538         return true;
539 }
540
541 static int poll_funcs_tevent_context_destructor(
542         struct poll_funcs_tevent_context *ctx);
543
544 static struct poll_funcs_tevent_context *poll_funcs_tevent_context_new(
545         TALLOC_CTX *mem_ctx, struct poll_funcs_state *state,
546         struct tevent_context *ev, unsigned slot)
547 {
548         struct poll_funcs_tevent_context *ctx;
549         size_t num_watches = talloc_array_length(state->watches);
550         size_t num_timeouts = talloc_array_length(state->timeouts);
551         size_t i;
552
553         ctx = talloc(mem_ctx, struct poll_funcs_tevent_context);
554         if (ctx == NULL) {
555                 return NULL;
556         }
557
558         ctx->handles = NULL;
559         ctx->state = state;
560         ctx->ev = ev;
561         ctx->slot = slot;
562
563         ctx->fdes = talloc_array(ctx, struct tevent_fd *, num_watches);
564         if (ctx->fdes == NULL) {
565                 goto fail;
566         }
567
568         for (i=0; i<num_watches; i++) {
569                 struct poll_watch *w = state->watches[i];
570
571                 if (w == NULL) {
572                         ctx->fdes[i] = NULL;
573                         continue;
574                 }
575                 ctx->fdes[i] = tevent_add_fd(ev, ctx->fdes, w->fd, w->events,
576                                              poll_funcs_fde_handler, w);
577                 if (ctx->fdes[i] == NULL) {
578                         goto fail;
579                 }
580         }
581
582         ctx->timers = talloc_array(ctx, struct tevent_timer *, num_timeouts);
583         if (ctx->timers == NULL) {
584                 goto fail;
585         }
586
587         for (i=0; i<num_timeouts; i++) {
588                 struct poll_timeout *t = state->timeouts[i];
589
590                 if (t == NULL) {
591                         ctx->timers[i] = NULL;
592                         continue;
593                 }
594                 ctx->timers[i] = tevent_add_timer(ctx->ev, ctx->timers, t->tv,
595                                                   poll_funcs_timer_handler, t);
596                 if (ctx->timers[i] == 0) {
597                         goto fail;
598                 }
599         }
600
601         talloc_set_destructor(ctx, poll_funcs_tevent_context_destructor);
602         return ctx;
603 fail:
604         TALLOC_FREE(ctx);
605         return NULL;
606 }
607
608 static int poll_funcs_tevent_context_destructor(
609         struct poll_funcs_tevent_context *ctx)
610 {
611         struct poll_funcs_tevent_handle *h;
612
613         ctx->state->contexts[ctx->slot] = NULL;
614
615         for (h = ctx->handles; h != NULL; h = h->next) {
616                 h->ctx = NULL;
617         }
618
619         return 0;
620 }
621
622 static void poll_funcs_fde_handler(struct tevent_context *ev,
623                                    struct tevent_fd *fde, uint16_t flags,
624                                    void *private_data)
625 {
626         struct poll_watch *w = talloc_get_type_abort(
627                 private_data, struct poll_watch);
628         short events = tevent_to_poll_events(flags);
629         w->callback(w, w->fd, events, w->private_data);
630 }
631
632 static int poll_funcs_tevent_handle_destructor(
633         struct poll_funcs_tevent_handle *handle);
634
635 void *poll_funcs_tevent_register(TALLOC_CTX *mem_ctx, struct poll_funcs *f,
636                                  struct tevent_context *ev)
637 {
638         struct poll_funcs_state *state = talloc_get_type_abort(
639                 f->private_data, struct poll_funcs_state);
640         struct poll_funcs_tevent_handle *handle;
641         size_t slot;
642
643         handle = talloc(mem_ctx, struct poll_funcs_tevent_handle);
644         if (handle == NULL) {
645                 return NULL;
646         }
647
648         if (!poll_funcs_context_slot_find(state, ev, &slot)) {
649                 goto fail;
650         }
651         if (state->contexts[slot] == NULL) {
652                 state->contexts[slot] = poll_funcs_tevent_context_new(
653                         state->contexts, state, ev, slot);
654                 if (state->contexts[slot] == NULL) {
655                         goto fail;
656                 }
657         }
658
659         handle->ctx = state->contexts[slot];
660         DLIST_ADD(handle->ctx->handles, handle);
661         talloc_set_destructor(handle, poll_funcs_tevent_handle_destructor);
662         return handle;
663 fail:
664         TALLOC_FREE(handle);
665         return NULL;
666 }
667
668 static int poll_funcs_tevent_handle_destructor(
669         struct poll_funcs_tevent_handle *handle)
670 {
671         if (handle->ctx == NULL) {
672                 return 0;
673         }
674         if (handle->ctx->handles == NULL) {
675                 abort();
676         }
677
678         DLIST_REMOVE(handle->ctx->handles, handle);
679
680         if (handle->ctx->handles == NULL) {
681                 TALLOC_FREE(handle->ctx);
682         }
683         return 0;
684 }