5b487d7341ebff0a550e183c28e6bc93e7e233fe
[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 && tvalp) {
500                 /* we don't care about a possible delay here */
501                 tevent_common_loop_timer_delay(ev);
502                 return 0;
503         }
504
505         if (ret == -1) {
506                 tevent_debug(ev, TEVENT_DEBUG_ERROR,
507                                 "port_get failed (%s)\n",
508                                 strerror(errno));
509                 return -1;
510         }
511
512         for (i = 0; i < nget; i++) {
513                 struct tevent_fd *mpx_fde = NULL;
514                 struct tevent_fd *fde = NULL;
515                 uint16_t flags = 0;
516                 struct port_associate_vals *val = talloc_get_type(events[i].portev_user,
517                                                         struct port_associate_vals);
518                 if (val == NULL) {
519                         tevent_debug(ev, TEVENT_DEBUG_ERROR,
520                                 "port_getn() gave bad data");
521                         return -1;
522                 }
523
524                 /* Mark this event as needing to be re-associated. */
525                 val->associated_event = false;
526
527                 fde = val->fde;
528
529                 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
530                         /*
531                          * Save off the multiplexed event in case we need
532                          * to use it to call the handler function.
533                          */
534                         mpx_fde = talloc_get_type_abort(fde->additional_data,
535                                                 struct tevent_fd);
536                 }
537
538                 if (events[i].portev_events & (POLLHUP|POLLERR)) {
539                         bool handled_fde = port_handle_hup_or_err(port_ev, fde);
540                         bool handled_mpx = port_handle_hup_or_err(port_ev, mpx_fde);
541
542                         if (handled_fde && handled_mpx) {
543                                 return port_update_event(port_ev, fde);
544                         }
545
546                         if (!handled_mpx) {
547                                 /*
548                                  * If the mpx event was the one that needs
549                                  * further handling, it's the TEVENT_FD_READ
550                                  * event so switch over and call that handler.
551                                  */
552                                 fde = mpx_fde;
553                                 mpx_fde = NULL;
554                         }
555                         flags |= TEVENT_FD_READ;
556                 }
557
558                 if (events[i].portev_events & POLLIN) {
559                         flags |= TEVENT_FD_READ;
560                 }
561                 if (events[i].portev_events & POLLOUT) {
562                         flags |= TEVENT_FD_WRITE;
563                 }
564
565                 if (flags & TEVENT_FD_WRITE) {
566                         if (fde->flags & TEVENT_FD_WRITE) {
567                                 mpx_fde = NULL;
568                         }
569                         if (mpx_fde && (mpx_fde->flags & TEVENT_FD_WRITE)) {
570                                 fde = mpx_fde;
571                                 mpx_fde = NULL;
572                         }
573
574                         if (mpx_fde) {
575                                 /* Ensure we got the right fde. */
576                                 if ((flags & fde->flags) == 0) {
577                                         fde = mpx_fde;
578                                         mpx_fde = NULL;
579                                 }
580                         }
581                 }
582
583                 /*
584                  * Make sure we only pass the flags
585                  * the handler is expecting.
586                  */
587                 flags &= fde->flags;
588                 if (flags) {
589                         fde->handler(ev, fde, flags, fde->private_data);
590                         break;
591                 }
592         }
593
594         return 0;
595 }
596
597
598 /*
599   create a port_event_context structure.
600 */
601 static int port_event_context_init(struct tevent_context *ev)
602 {
603         int ret;
604         struct port_event_context *port_ev;
605
606         /*
607          * We might be called during tevent_re_initialise()
608          * which means we need to free our old additional_data.
609          */
610         TALLOC_FREE(ev->additional_data);
611
612         port_ev = talloc_zero(ev, struct port_event_context);
613         if (!port_ev) {
614                 return -1;
615         }
616         port_ev->ev = ev;
617         port_ev->port_fd = -1;
618         port_ev->pid = (pid_t)-1;
619
620         ret = port_init_ctx(port_ev);
621         if (ret != 0) {
622                 talloc_free(port_ev);
623                 return ret;
624         }
625
626         ev->additional_data = port_ev;
627         return 0;
628 }
629
630 /*
631   destroy an fd_event
632 */
633 static int port_event_fd_destructor(struct tevent_fd *fde)
634 {
635         struct tevent_context *ev = fde->event_ctx;
636         struct port_event_context *port_ev = NULL;
637         struct tevent_fd *mpx_fde = NULL;
638         int flags = (int)fde->flags;
639
640         if (ev == NULL) {
641                 return tevent_common_fd_destructor(fde);
642         }
643
644         port_ev = talloc_get_type_abort(ev->additional_data,
645                                          struct port_event_context);
646
647         DLIST_REMOVE(ev->fd_events, fde);
648
649         if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
650                 mpx_fde = talloc_get_type_abort(fde->additional_data,
651                                                 struct tevent_fd);
652
653                 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
654                 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
655
656                 fde->additional_data = NULL;
657                 mpx_fde->additional_data = NULL;
658
659                 fde->additional_flags &= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
660         }
661
662         (void)port_check_reopen(port_ev);
663
664         if (mpx_fde != NULL) {
665                 (void)port_update_event(port_ev, mpx_fde);
666         }
667
668         fde->flags = 0;
669         (void)port_update_event(port_ev, fde);
670         fde->flags = flags;
671
672         return tevent_common_fd_destructor(fde);
673 }
674
675 /*
676   add a fd based event
677   return NULL on failure (memory allocation error)
678 */
679 static struct tevent_fd *port_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
680                                             int fd, uint16_t flags,
681                                             tevent_fd_handler_t handler,
682                                             void *private_data,
683                                             const char *handler_name,
684                                             const char *location)
685 {
686         struct port_event_context *port_ev =
687                                 talloc_get_type_abort(ev->additional_data,
688                                 struct port_event_context);
689         struct tevent_fd *fde;
690
691         fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
692                                    handler, private_data,
693                                    handler_name, location);
694         if (!fde) {
695                 return NULL;
696         }
697
698         talloc_set_destructor(fde, port_event_fd_destructor);
699
700         if (port_check_reopen(port_ev) != 0) {
701                 TALLOC_FREE(fde);
702                 return NULL;
703         }
704
705         if (port_update_event(port_ev, fde) != 0) {
706                 TALLOC_FREE(fde);
707                 return NULL;
708         }
709
710         return fde;
711 }
712
713 /*
714   set the fd event flags
715 */
716 static void port_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
717 {
718         struct tevent_context *ev;
719         struct port_event_context *port_ev;
720
721         if (fde->flags == flags) {
722                 return;
723         }
724
725         ev = fde->event_ctx;
726         port_ev = talloc_get_type_abort(ev->additional_data,
727                                 struct port_event_context);
728
729         fde->flags = flags;
730
731         (void)port_check_reopen(port_ev);
732         (void)port_update_event(port_ev, fde);
733 }
734
735 /*
736   do a single event loop using the events defined in ev
737 */
738 static int port_event_loop_once(struct tevent_context *ev, const char *location)
739 {
740         struct port_event_context *port_ev = talloc_get_type(ev->additional_data,
741                                                            struct port_event_context);
742         struct timeval tval;
743
744         if (ev->signal_events &&
745             tevent_common_check_signal(ev)) {
746                 return 0;
747         }
748
749         if (ev->immediate_events &&
750             tevent_common_loop_immediate(ev)) {
751                 return 0;
752         }
753
754         tval = tevent_common_loop_timer_delay(ev);
755         if (tevent_timeval_is_zero(&tval)) {
756                 return 0;
757         }
758
759         if (port_check_reopen(port_ev) != 0) {
760                 errno = EINVAL;
761                 return -1;
762         }
763         return port_event_loop(port_ev, &tval);
764 }
765
766 static const struct tevent_ops port_event_ops = {
767         .context_init           = port_event_context_init,
768         .add_fd                 = port_event_add_fd,
769         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
770         .get_fd_flags           = tevent_common_fd_get_flags,
771         .set_fd_flags           = port_event_set_fd_flags,
772         .add_timer              = tevent_common_add_timer_v2,
773         .schedule_immediate     = tevent_common_schedule_immediate,
774         .add_signal             = tevent_common_add_signal,
775         .loop_once              = port_event_loop_once,
776         .loop_wait              = tevent_common_loop_wait,
777 };
778
779 _PRIVATE_ bool tevent_port_init(void)
780 {
781         if (!tevent_register_backend("port", &port_event_ops)) {
782                 return false;
783         }
784         tevent_set_default_backend("port");
785         return true;
786 }