lib:mscat: Use size_t for len value to fix build issue
[samba.git] / lib / tevent / tevent_port.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Main select loop and event handling - Solaris port implementation.
5    Losely based on the Linux epoll backend.
6
7    Copyright (C) Jeremy Allison         2013
8
9      ** NOTE! The following LGPL license applies to the tevent
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "replace.h"
28 #include "system/filesys.h"
29 #include "system/select.h"
30 #include "tevent.h"
31 #include "tevent_internal.h"
32 #include "tevent_util.h"
33
34 struct port_associate_vals {
35         struct port_associate_vals *prev, *next;
36         struct port_event_context *port_ev;
37         int events;
38         struct tevent_fd *fde;
39         bool associated_event;
40 };
41
42 struct port_event_context {
43         /* a pointer back to the generic event_context */
44         struct tevent_context *ev;
45
46         /* This is the handle from port_create */
47         int port_fd;
48
49         pid_t pid;
50
51         /* List of associations. */
52         struct port_associate_vals *po_vals;
53 };
54
55 #define PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION (1<<0)
56 #define PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR    (1<<1)
57 #define PORT_ADDITIONAL_FD_FLAG_GOT_ERROR       (1<<2)
58 #define PORT_ADDITIONAL_FD_FLAG_HAS_MPX         (1<<3)
59
60 /*
61   Map from TEVENT_FD_* to POLLIN/POLLOUT
62 */
63 static int port_map_flags(uint16_t flags)
64 {
65         int ret = 0;
66         if (flags & TEVENT_FD_READ) ret |= (POLLIN | POLLERR | POLLHUP);
67         if (flags & TEVENT_FD_WRITE) ret |= (POLLOUT | POLLERR | POLLHUP);
68         return ret;
69 }
70
71 /*
72  Free the port fd
73 */
74 static int port_ctx_destructor(struct port_event_context *port_ev)
75 {
76         close(port_ev->port_fd);
77         port_ev->port_fd = -1;
78         return 0;
79 }
80
81 /*
82  Init the port fd
83 */
84 static int port_init_ctx(struct port_event_context *port_ev)
85 {
86         port_ev->port_fd = port_create();
87         if (port_ev->port_fd == -1) {
88                 tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL,
89                              "Failed to create port handle.\n");
90                 return -1;
91         }
92
93         if (!ev_set_close_on_exec(port_ev->port_fd)) {
94                 tevent_debug(port_ev->ev, TEVENT_DEBUG_WARNING,
95                              "Failed to set close-on-exec, file descriptor may be leaked to children.\n");
96         }
97
98         port_ev->pid = getpid();
99         talloc_set_destructor(port_ev, port_ctx_destructor);
100
101         return 0;
102 }
103
104 /*
105  Functions to manage the lower level cache of associated events on the port_fd.
106 */
107
108 static int port_associate_vals_destructor(struct port_associate_vals *val)
109 {
110         DLIST_REMOVE(val->port_ev->po_vals, val);
111         memset(val, '\0', sizeof(struct port_associate_vals));
112         return 0;
113 }
114
115 /*
116  * TODO: As the port_association is per-fde, it should be possible to store it
117  * directly in fde->additional_data, alongside any multiplexed-fde. That way the
118  * lookup on store and delete would be avoided, and associate_all_events() could
119  * walk the ev->fd_events list.
120  */
121 static bool store_port_association(struct port_event_context *port_ev,
122                                 struct tevent_fd *fde,
123                                 int events)
124 {
125         struct port_associate_vals *val;
126
127         for (val = port_ev->po_vals; val; val = val->next) {
128                 if (val->fde->fd == fde->fd) {
129                         /* Association already attached to fd. */
130                         if (val->events != events) {
131                                 val->events = events;
132                                 val->associated_event = false;
133                         }
134                         return true;
135                 }
136         }
137
138         val = talloc_zero(port_ev, struct port_associate_vals);
139         if (val == NULL) {
140                 return false;
141         }
142
143         val->port_ev = port_ev;
144         val->fde = fde;
145         val->events = events;
146         val->associated_event = false;
147
148         DLIST_ADD(port_ev->po_vals, val);
149         talloc_set_destructor(val, port_associate_vals_destructor);
150
151         return true;
152 }
153
154 static void delete_port_association(struct port_event_context *port_ev,
155                                 struct tevent_fd *fde)
156 {
157         struct port_associate_vals *val;
158
159         for (val = port_ev->po_vals; val; val = val->next) {
160                 if (val->fde == fde) {
161                         if (val->associated_event) {
162                                 (void)port_dissociate(port_ev->port_fd,
163                                                         PORT_SOURCE_FD,
164                                                         fde->fd);
165                         }
166                         talloc_free(val);
167                         return;
168                 }
169         }
170 }
171
172 static int associate_all_events(struct port_event_context *port_ev)
173 {
174         struct port_associate_vals *val;
175
176         for (val = port_ev->po_vals; val; val = val->next) {
177                 int ret;
178                 if (val->associated_event) {
179                         continue;
180                 }
181                 ret = port_associate(port_ev->port_fd,
182                                         PORT_SOURCE_FD,
183                                         (uintptr_t)val->fde->fd,
184                                         val->events,
185                                         (void *)val);
186                 if (ret != 0) {
187                         return -1;
188                 }
189                 val->associated_event = true;
190         }
191         return 0;
192 }
193
194 static int port_update_event(struct port_event_context *port_ev, struct tevent_fd *fde);
195
196 /*
197   Reopen the port handle when our pid changes.
198  */
199 static int port_check_reopen(struct port_event_context *port_ev)
200 {
201         struct tevent_fd *fde;
202
203         if (port_ev->pid == getpid()) {
204                 return 0;
205         }
206
207         close(port_ev->port_fd);
208         port_ev->port_fd = port_create();
209         if (port_ev->port_fd == -1) {
210                 tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL,
211                                 "port_create() failed");
212                 return -1;
213         }
214
215         if (!ev_set_close_on_exec(port_ev->port_fd)) {
216                 tevent_debug(port_ev->ev, TEVENT_DEBUG_WARNING,
217                              "Failed to set close-on-exec, file descriptor may be leaked to children.\n");
218         }
219
220         port_ev->pid = getpid();
221         for (fde=port_ev->ev->fd_events;fde;fde=fde->next) {
222                 fde->additional_flags &= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
223                 if (port_update_event(port_ev, fde) != 0) {
224                         return -1;
225                 }
226         }
227         return 0;
228 }
229
230 /*
231  * Solaris ports cannot add the same file descriptor twice, once
232  * with read, once with write which is allowed by the tevent backend.
233  * Multiplex the existing fde, flag it as such so we can search for the
234  * correct fde on event triggering.
235  */
236
237 static void port_setup_multiplex_fd(struct port_event_context *port_ev,
238                                 struct tevent_fd *add_fde,
239                                 struct tevent_fd *mpx_fde)
240 {
241         /*
242          * Make each fde->additional_data pointers point at each other
243          * so we can look them up from each other. They are now paired.
244          */
245         mpx_fde->additional_data = add_fde;
246         add_fde->additional_data = mpx_fde;
247
248         /* Now flag both fde's as being multiplexed. */
249         mpx_fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
250         add_fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
251
252         /* We need to keep the GOT_ERROR flag. */
253         if (mpx_fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_GOT_ERROR) {
254                 add_fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_GOT_ERROR;
255         }
256 }
257
258 /*
259  Add the port event to the given fd_event,
260  Or modify an existing event.
261 */
262
263 static int port_add_event(struct port_event_context *port_ev, struct tevent_fd *fde)
264 {
265         int flags = port_map_flags(fde->flags);
266         struct tevent_fd *mpx_fde = NULL;
267
268         fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
269         fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
270
271         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
272                 /*
273                  * This is already a multiplexed fde, we need to include both
274                  * flags in the modified event.
275                  */
276                 mpx_fde = talloc_get_type_abort(fde->additional_data,
277                                                 struct tevent_fd);
278
279                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
280                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
281
282                 flags |= port_map_flags(mpx_fde->flags);
283         } else {
284                 /*
285                  * Not (yet) a multiplexed event. See if there
286                  * is already an event with the same fd.
287                  */
288                 for (mpx_fde = port_ev->ev->fd_events; mpx_fde; mpx_fde = mpx_fde->next) {
289                         if (mpx_fde->fd != fde->fd) {
290                                 continue;
291                         }
292                         if (mpx_fde == fde) {
293                                 continue;
294                         }
295                         /* Same fd. */
296                         break;
297                 }
298                 if (mpx_fde) {
299                         if (mpx_fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
300                                 /* Logic error. Can't have more then 2 multiplexed fde's. */
301                                 tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL,
302                                         "multiplex fde for fd[%d] is already multiplexed\n",
303                                         mpx_fde->fd);
304                                 return -1;
305                         }
306                         flags |= port_map_flags(mpx_fde->flags);
307                 }
308         }
309
310         if (!store_port_association(port_ev,
311                                 fde,
312                                 flags)) {
313                 tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL,
314                         "store_port_association failed for fd[%d]\n",
315                         fde->fd);
316                 return -1;
317         }
318
319         /* Note we have an association now. */
320         fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
321         /* Only if we want to read do we tell the event handler about errors. */
322         if (fde->flags & TEVENT_FD_READ) {
323                 fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
324         }
325         if (mpx_fde == NULL) {
326                 return 0;
327         }
328         /* Set up the multiplex pointer. Does no harm if already multiplexed. */
329         port_setup_multiplex_fd(port_ev,
330                                 fde,
331                                 mpx_fde);
332
333         mpx_fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
334         /* Only if we want to read do we tell the event handler about errors. */
335         if (mpx_fde->flags & TEVENT_FD_READ) {
336                 mpx_fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
337         }
338
339         return 0;
340 }
341
342 /*
343  Delete the port association for the given fd_event.
344 */
345
346 static void port_del_event(struct port_event_context *port_ev, struct tevent_fd *fde)
347 {
348         struct tevent_fd *mpx_fde = NULL;
349
350         fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
351         fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
352
353         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
354                 /*
355                  * This is a multiplexed fde, we need to remove
356                  * both associations.
357                  */
358                 mpx_fde = talloc_get_type_abort(fde->additional_data,
359                                                 struct tevent_fd);
360
361                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
362                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
363                 mpx_fde->additional_data = NULL;
364
365                 fde->additional_data = NULL;
366         }
367         delete_port_association(port_ev, fde);
368 }
369
370 /*
371  Add or remove the port event from the given fd_event
372 */
373 static int port_update_event(struct port_event_context *port_ev, struct tevent_fd *fde)
374 {
375         bool got_error = (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_GOT_ERROR);
376         bool want_read = (fde->flags & TEVENT_FD_READ);
377         bool want_write = (fde->flags & TEVENT_FD_WRITE);
378         struct tevent_fd *mpx_fde = NULL;
379
380         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
381                 /*
382                  * work out what the multiplexed fde wants.
383                  */
384                 mpx_fde = talloc_get_type_abort(fde->additional_data,
385                                                 struct tevent_fd);
386                 if (mpx_fde->flags & TEVENT_FD_READ) {
387                         want_read = true;
388                 }
389                 if (mpx_fde->flags & TEVENT_FD_WRITE) {
390                         want_write = true;
391                 }
392         }
393
394         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION) {
395                 /* There's already an association. */
396                 if (want_read || (want_write && !got_error)) {
397                         return port_add_event(port_ev, fde);
398                 }
399                 /*
400                  * If we want to match the select behavior, we need to remove the port event
401                  * when the caller isn't interested in events.
402                  */
403                 port_del_event(port_ev, fde);
404                 return 0;
405         }
406
407         /* There's no port event attached to the fde. */
408         if (want_read || (want_write && !got_error)) {
409                 return port_add_event(port_ev, fde);
410         }
411         return 0;
412 }
413
414 /*
415  Cope with port_get returning EPOLLHP|EPOLLERR on an association.
416  Return true if there's nothing else to do, false if this event
417  needs further handling.
418 */
419
420 static bool port_handle_hup_or_err(struct port_event_context *port_ev,
421                                 struct tevent_fd *fde)
422 {
423         if (fde == NULL) {
424                 return true;
425         }
426
427         fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_GOT_ERROR;
428         /*
429          * If we only wait for TEVENT_FD_WRITE, we should not tell the
430          * event handler about it, and remove the port association,
431          * as we only report error when waiting for read events,
432          * to match the select() behavior.
433          */
434         if (!(fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
435                 /*
436                  * Do the same as the poll backend and
437                  * remove the writable flag.
438                  */
439                 fde->flags &= ~TEVENT_FD_WRITE;
440                 return true;
441         }
442         /* This has TEVENT_FD_READ set, we're not finished. */
443         return false;
444 }
445
446 /*
447   Event loop handling using Solaris ports.
448 */
449 static int port_event_loop(struct port_event_context *port_ev, struct timeval *tvalp)
450 {
451         int ret;
452 #define MAXEVENTS 1
453         port_event_t events[MAXEVENTS];
454         uint_t nget = 1;
455         uint_t max_events = MAXEVENTS;
456         uint_t i;
457         int port_errno;
458         struct timespec ts;
459         struct tevent_context *ev = port_ev->ev;
460
461         if (tvalp) {
462                 ts.tv_sec = tvalp->tv_sec;
463                 ts.tv_nsec = tvalp->tv_usec * 1000;
464         }
465
466         if (port_ev->ev->signal_events &&
467             tevent_common_check_signal(ev)) {
468                 return 0;
469         }
470
471         /*
472          * Solaris triggers sending the event to the port
473          * at the time the port association is done. Postpone
474          * associating fd's until just before we get the events,
475          * otherwise we can deadlock.
476          */
477
478         if (associate_all_events(port_ev) != 0) {
479                 return -1;
480         }
481
482         tevent_trace_point_callback(ev, TEVENT_TRACE_BEFORE_WAIT);
483         ret = port_getn(port_ev->port_fd, events, max_events, &nget, &ts);
484         port_errno = errno;
485         tevent_trace_point_callback(ev, TEVENT_TRACE_AFTER_WAIT);
486
487         if (ret == -1 && port_errno == EINTR) {
488                 if (ev->signal_events) {
489                         tevent_common_check_signal(ev);
490                 }
491                 /*
492                  * If no signal handlers we got an unsolicited
493                  * signal wakeup. This can happen with epoll
494                  * too. Just return and ignore.
495                  */
496                 return 0;
497         }
498
499         if (ret == -1 && port_errno == ETIME) {
500                 /*
501                  * If errno is set to ETIME it is possible that we still got an event.
502                  * In that case we need to go through the processing loop so that we
503                  * reassociate the received event with the port or the association will
504                  * be lost so check the value of nget is 0 before returning.
505                  */
506                 if (nget == 0) {
507                         /* we don't care about a possible delay here */
508                         tevent_common_loop_timer_delay(ev);
509                         return 0;
510                 }
511                 /*
512                  * Set the return value to 0 since we do not actually have an error and we
513                  * do have events that need to be processed.  This keeps us from getting
514                  * caught in the generic error test.
515                  */
516                 ret = 0;
517         }
518
519         if (ret == -1) {
520                 tevent_debug(ev, TEVENT_DEBUG_ERROR,
521                                 "port_get failed (%s)\n",
522                                 strerror(errno));
523                 return -1;
524         }
525
526         for (i = 0; i < nget; i++) {
527                 struct tevent_fd *mpx_fde = NULL;
528                 struct tevent_fd *fde = NULL;
529                 uint16_t flags = 0;
530                 struct port_associate_vals *val = talloc_get_type(events[i].portev_user,
531                                                         struct port_associate_vals);
532                 if (val == NULL) {
533                         tevent_debug(ev, TEVENT_DEBUG_ERROR,
534                                 "port_getn() gave bad data");
535                         return -1;
536                 }
537
538                 /* Mark this event as needing to be re-associated. */
539                 val->associated_event = false;
540
541                 fde = val->fde;
542
543                 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
544                         /*
545                          * Save off the multiplexed event in case we need
546                          * to use it to call the handler function.
547                          */
548                         mpx_fde = talloc_get_type_abort(fde->additional_data,
549                                                 struct tevent_fd);
550                 }
551
552                 if (events[i].portev_events & (POLLHUP|POLLERR)) {
553                         bool handled_fde = port_handle_hup_or_err(port_ev, fde);
554                         bool handled_mpx = port_handle_hup_or_err(port_ev, mpx_fde);
555
556                         if (handled_fde && handled_mpx) {
557                                 return port_update_event(port_ev, fde);
558                         }
559
560                         if (!handled_mpx) {
561                                 /*
562                                  * If the mpx event was the one that needs
563                                  * further handling, it's the TEVENT_FD_READ
564                                  * event so switch over and call that handler.
565                                  */
566                                 fde = mpx_fde;
567                                 mpx_fde = NULL;
568                         }
569                         flags |= TEVENT_FD_READ;
570                 }
571
572                 if (events[i].portev_events & POLLIN) {
573                         flags |= TEVENT_FD_READ;
574                 }
575                 if (events[i].portev_events & POLLOUT) {
576                         flags |= TEVENT_FD_WRITE;
577                 }
578
579                 if (flags & TEVENT_FD_WRITE) {
580                         if (fde->flags & TEVENT_FD_WRITE) {
581                                 mpx_fde = NULL;
582                         }
583                         if (mpx_fde && (mpx_fde->flags & TEVENT_FD_WRITE)) {
584                                 fde = mpx_fde;
585                                 mpx_fde = NULL;
586                         }
587
588                         if (mpx_fde) {
589                                 /* Ensure we got the right fde. */
590                                 if ((flags & fde->flags) == 0) {
591                                         fde = mpx_fde;
592                                         mpx_fde = NULL;
593                                 }
594                         }
595                 }
596
597                 /*
598                  * Make sure we only pass the flags
599                  * the handler is expecting.
600                  */
601                 flags &= fde->flags;
602                 if (flags) {
603                         return tevent_common_invoke_fd_handler(fde, flags, NULL);
604                 }
605         }
606
607         return 0;
608 }
609
610
611 /*
612   create a port_event_context structure.
613 */
614 static int port_event_context_init(struct tevent_context *ev)
615 {
616         int ret;
617         struct port_event_context *port_ev;
618
619         /*
620          * We might be called during tevent_re_initialise()
621          * which means we need to free our old additional_data.
622          */
623         TALLOC_FREE(ev->additional_data);
624
625         port_ev = talloc_zero(ev, struct port_event_context);
626         if (!port_ev) {
627                 return -1;
628         }
629         port_ev->ev = ev;
630         port_ev->port_fd = -1;
631         port_ev->pid = (pid_t)-1;
632
633         ret = port_init_ctx(port_ev);
634         if (ret != 0) {
635                 talloc_free(port_ev);
636                 return ret;
637         }
638
639         ev->additional_data = port_ev;
640         return 0;
641 }
642
643 /*
644   destroy an fd_event
645 */
646 static int port_event_fd_destructor(struct tevent_fd *fde)
647 {
648         struct tevent_context *ev = fde->event_ctx;
649         struct port_event_context *port_ev = NULL;
650         struct tevent_fd *mpx_fde = NULL;
651         int flags = (int)fde->flags;
652
653         if (ev == NULL) {
654                 return tevent_common_fd_destructor(fde);
655         }
656
657         port_ev = talloc_get_type_abort(ev->additional_data,
658                                          struct port_event_context);
659
660         DLIST_REMOVE(ev->fd_events, fde);
661
662         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
663                 mpx_fde = talloc_get_type_abort(fde->additional_data,
664                                                 struct tevent_fd);
665
666                 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
667                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
668
669                 fde->additional_data = NULL;
670                 mpx_fde->additional_data = NULL;
671
672                 fde->additional_flags &= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
673         }
674
675         (void)port_check_reopen(port_ev);
676
677         if (mpx_fde != NULL) {
678                 (void)port_update_event(port_ev, mpx_fde);
679         }
680
681         fde->flags = 0;
682         (void)port_update_event(port_ev, fde);
683         fde->flags = flags;
684
685         return tevent_common_fd_destructor(fde);
686 }
687
688 /*
689   add a fd based event
690   return NULL on failure (memory allocation error)
691 */
692 static struct tevent_fd *port_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
693                                             int fd, uint16_t flags,
694                                             tevent_fd_handler_t handler,
695                                             void *private_data,
696                                             const char *handler_name,
697                                             const char *location)
698 {
699         struct port_event_context *port_ev =
700                                 talloc_get_type_abort(ev->additional_data,
701                                 struct port_event_context);
702         struct tevent_fd *fde;
703
704         fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
705                                    handler, private_data,
706                                    handler_name, location);
707         if (!fde) {
708                 return NULL;
709         }
710
711         talloc_set_destructor(fde, port_event_fd_destructor);
712
713         if (port_check_reopen(port_ev) != 0) {
714                 TALLOC_FREE(fde);
715                 return NULL;
716         }
717
718         if (port_update_event(port_ev, fde) != 0) {
719                 TALLOC_FREE(fde);
720                 return NULL;
721         }
722
723         return fde;
724 }
725
726 /*
727   set the fd event flags
728 */
729 static void port_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
730 {
731         struct tevent_context *ev;
732         struct port_event_context *port_ev;
733
734         if (fde->flags == flags) {
735                 return;
736         }
737
738         ev = fde->event_ctx;
739         port_ev = talloc_get_type_abort(ev->additional_data,
740                                 struct port_event_context);
741
742         fde->flags = flags;
743
744         (void)port_check_reopen(port_ev);
745         (void)port_update_event(port_ev, fde);
746 }
747
748 /*
749   do a single event loop using the events defined in ev
750 */
751 static int port_event_loop_once(struct tevent_context *ev, const char *location)
752 {
753         struct port_event_context *port_ev = talloc_get_type(ev->additional_data,
754                                                            struct port_event_context);
755         struct timeval tval;
756
757         if (ev->signal_events &&
758             tevent_common_check_signal(ev)) {
759                 return 0;
760         }
761
762         if (ev->threaded_contexts != NULL) {
763                 tevent_common_threaded_activate_immediate(ev);
764         }
765
766         if (ev->immediate_events &&
767             tevent_common_loop_immediate(ev)) {
768                 return 0;
769         }
770
771         tval = tevent_common_loop_timer_delay(ev);
772         if (tevent_timeval_is_zero(&tval)) {
773                 return 0;
774         }
775
776         if (port_check_reopen(port_ev) != 0) {
777                 errno = EINVAL;
778                 return -1;
779         }
780         return port_event_loop(port_ev, &tval);
781 }
782
783 static const struct tevent_ops port_event_ops = {
784         .context_init           = port_event_context_init,
785         .add_fd                 = port_event_add_fd,
786         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
787         .get_fd_flags           = tevent_common_fd_get_flags,
788         .set_fd_flags           = port_event_set_fd_flags,
789         .add_timer              = tevent_common_add_timer_v2,
790         .schedule_immediate     = tevent_common_schedule_immediate,
791         .add_signal             = tevent_common_add_signal,
792         .loop_once              = port_event_loop_once,
793         .loop_wait              = tevent_common_loop_wait,
794 };
795
796 _PRIVATE_ bool tevent_port_init(void)
797 {
798         if (!tevent_register_backend("port", &port_event_ops)) {
799                 return false;
800         }
801         tevent_set_default_backend("port");
802         return true;
803 }