WHATSNEW: Add an entry for the LDB whole DB locking issue
[Samba.git] / lib / tevent / tevent_port.c
blob8cf9fd1a0de7aa9bc6abea8b21212cf2b420041c
1 /*
2 Unix SMB/CIFS implementation.
4 Main select loop and event handling - Solaris port implementation.
5 Losely based on the Linux epoll backend.
7 Copyright (C) Jeremy Allison 2013
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
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.
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.
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/>.
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"
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;
42 struct port_event_context {
43 /* a pointer back to the generic event_context */
44 struct tevent_context *ev;
46 /* This is the handle from port_create */
47 int port_fd;
49 pid_t pid;
51 /* List of associations. */
52 struct port_associate_vals *po_vals;
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)
61 Map from TEVENT_FD_* to POLLIN/POLLOUT
63 static int port_map_flags(uint16_t flags)
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;
72 Free the port fd
74 static int port_ctx_destructor(struct port_event_context *port_ev)
76 close(port_ev->port_fd);
77 port_ev->port_fd = -1;
78 return 0;
82 Init the port fd
84 static int port_init_ctx(struct port_event_context *port_ev)
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;
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");
98 port_ev->pid = getpid();
99 talloc_set_destructor(port_ev, port_ctx_destructor);
101 return 0;
105 Functions to manage the lower level cache of associated events on the port_fd.
108 static int port_associate_vals_destructor(struct port_associate_vals *val)
110 DLIST_REMOVE(val->port_ev->po_vals, val);
111 memset(val, '\0', sizeof(struct port_associate_vals));
112 return 0;
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.
121 static bool store_port_association(struct port_event_context *port_ev,
122 struct tevent_fd *fde,
123 int events)
125 struct port_associate_vals *val;
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;
134 return true;
138 val = talloc_zero(port_ev, struct port_associate_vals);
139 if (val == NULL) {
140 return false;
143 val->port_ev = port_ev;
144 val->fde = fde;
145 val->events = events;
146 val->associated_event = false;
148 DLIST_ADD(port_ev->po_vals, val);
149 talloc_set_destructor(val, port_associate_vals_destructor);
151 return true;
154 static void delete_port_association(struct port_event_context *port_ev,
155 struct tevent_fd *fde)
157 struct port_associate_vals *val;
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);
166 talloc_free(val);
167 return;
172 static int associate_all_events(struct port_event_context *port_ev)
174 struct port_associate_vals *val;
176 for (val = port_ev->po_vals; val; val = val->next) {
177 int ret;
178 if (val->associated_event) {
179 continue;
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;
189 val->associated_event = true;
191 return 0;
194 static int port_update_event(struct port_event_context *port_ev, struct tevent_fd *fde);
197 Reopen the port handle when our pid changes.
199 static int port_check_reopen(struct port_event_context *port_ev)
201 struct tevent_fd *fde;
203 if (port_ev->pid == getpid()) {
204 return 0;
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;
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");
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;
227 return 0;
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.
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)
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.
245 mpx_fde->additional_data = add_fde;
246 add_fde->additional_data = mpx_fde;
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;
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;
259 Add the port event to the given fd_event,
260 Or modify an existing event.
263 static int port_add_event(struct port_event_context *port_ev, struct tevent_fd *fde)
265 int flags = port_map_flags(fde->flags);
266 struct tevent_fd *mpx_fde = NULL;
268 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
269 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
271 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
273 * This is already a multiplexed fde, we need to include both
274 * flags in the modified event.
276 mpx_fde = talloc_get_type_abort(fde->additional_data,
277 struct tevent_fd);
279 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
280 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
282 flags |= port_map_flags(mpx_fde->flags);
283 } else {
285 * Not (yet) a multiplexed event. See if there
286 * is already an event with the same fd.
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;
292 if (mpx_fde == fde) {
293 continue;
295 /* Same fd. */
296 break;
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;
306 flags |= port_map_flags(mpx_fde->flags);
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;
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;
325 if (mpx_fde == NULL) {
326 return 0;
328 /* Set up the multiplex pointer. Does no harm if already multiplexed. */
329 port_setup_multiplex_fd(port_ev,
330 fde,
331 mpx_fde);
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;
339 return 0;
343 Delete the port association for the given fd_event.
346 static void port_del_event(struct port_event_context *port_ev, struct tevent_fd *fde)
348 struct tevent_fd *mpx_fde = NULL;
350 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
351 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR;
353 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
355 * This is a multiplexed fde, we need to remove
356 * both associations.
358 mpx_fde = talloc_get_type_abort(fde->additional_data,
359 struct tevent_fd);
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;
365 fde->additional_data = NULL;
367 delete_port_association(port_ev, fde);
371 Add or remove the port event from the given fd_event
373 static int port_update_event(struct port_event_context *port_ev, struct tevent_fd *fde)
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;
380 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
382 * work out what the multiplexed fde wants.
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;
389 if (mpx_fde->flags & TEVENT_FD_WRITE) {
390 want_write = true;
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);
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.
403 port_del_event(port_ev, fde);
404 return 0;
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);
411 return 0;
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.
420 static bool port_handle_hup_or_err(struct port_event_context *port_ev,
421 struct tevent_fd *fde)
423 if (fde == NULL) {
424 return true;
427 fde->additional_flags |= PORT_ADDITIONAL_FD_FLAG_GOT_ERROR;
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.
434 if (!(fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR)) {
436 * Do the same as the poll backend and
437 * remove the writable flag.
439 fde->flags &= ~TEVENT_FD_WRITE;
440 return true;
442 /* This has TEVENT_FD_READ set, we're not finished. */
443 return false;
447 Event loop handling using Solaris ports.
449 static int port_event_loop(struct port_event_context *port_ev, struct timeval *tvalp)
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;
461 if (tvalp) {
462 ts.tv_sec = tvalp->tv_sec;
463 ts.tv_nsec = tvalp->tv_usec * 1000;
466 if (port_ev->ev->signal_events &&
467 tevent_common_check_signal(ev)) {
468 return 0;
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.
478 if (associate_all_events(port_ev) != 0) {
479 return -1;
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);
487 if (ret == -1 && port_errno == EINTR) {
488 if (ev->signal_events) {
489 tevent_common_check_signal(ev);
492 * If no signal handlers we got an unsolicited
493 * signal wakeup. This can happen with epoll
494 * too. Just return and ignore.
496 return 0;
499 if (ret == -1 && port_errno == ETIME) {
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.
506 if (nget == 0) {
507 /* we don't care about a possible delay here */
508 tevent_common_loop_timer_delay(ev);
509 return 0;
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.
516 ret = 0;
519 if (ret == -1) {
520 tevent_debug(ev, TEVENT_DEBUG_ERROR,
521 "port_get failed (%s)\n",
522 strerror(errno));
523 return -1;
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;
538 /* Mark this event as needing to be re-associated. */
539 val->associated_event = false;
541 fde = val->fde;
543 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
545 * Save off the multiplexed event in case we need
546 * to use it to call the handler function.
548 mpx_fde = talloc_get_type_abort(fde->additional_data,
549 struct tevent_fd);
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);
556 if (handled_fde && handled_mpx) {
557 return port_update_event(port_ev, fde);
560 if (!handled_mpx) {
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.
566 fde = mpx_fde;
567 mpx_fde = NULL;
569 flags |= TEVENT_FD_READ;
572 if (events[i].portev_events & POLLIN) {
573 flags |= TEVENT_FD_READ;
575 if (events[i].portev_events & POLLOUT) {
576 flags |= TEVENT_FD_WRITE;
579 if (flags & TEVENT_FD_WRITE) {
580 if (fde->flags & TEVENT_FD_WRITE) {
581 mpx_fde = NULL;
583 if (mpx_fde && (mpx_fde->flags & TEVENT_FD_WRITE)) {
584 fde = mpx_fde;
585 mpx_fde = NULL;
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;
598 * Make sure we only pass the flags
599 * the handler is expecting.
601 flags &= fde->flags;
602 if (flags) {
603 fde->handler(ev, fde, flags, fde->private_data);
604 break;
608 return 0;
613 create a port_event_context structure.
615 static int port_event_context_init(struct tevent_context *ev)
617 int ret;
618 struct port_event_context *port_ev;
621 * We might be called during tevent_re_initialise()
622 * which means we need to free our old additional_data.
624 TALLOC_FREE(ev->additional_data);
626 port_ev = talloc_zero(ev, struct port_event_context);
627 if (!port_ev) {
628 return -1;
630 port_ev->ev = ev;
631 port_ev->port_fd = -1;
632 port_ev->pid = (pid_t)-1;
634 ret = port_init_ctx(port_ev);
635 if (ret != 0) {
636 talloc_free(port_ev);
637 return ret;
640 ev->additional_data = port_ev;
641 return 0;
645 destroy an fd_event
647 static int port_event_fd_destructor(struct tevent_fd *fde)
649 struct tevent_context *ev = fde->event_ctx;
650 struct port_event_context *port_ev = NULL;
651 struct tevent_fd *mpx_fde = NULL;
652 int flags = (int)fde->flags;
654 if (ev == NULL) {
655 return tevent_common_fd_destructor(fde);
658 port_ev = talloc_get_type_abort(ev->additional_data,
659 struct port_event_context);
661 DLIST_REMOVE(ev->fd_events, fde);
663 if (fde->additional_flags & PORT_ADDITIONAL_FD_FLAG_HAS_MPX) {
664 mpx_fde = talloc_get_type_abort(fde->additional_data,
665 struct tevent_fd);
667 fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
668 mpx_fde->additional_flags &= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX;
670 fde->additional_data = NULL;
671 mpx_fde->additional_data = NULL;
673 fde->additional_flags &= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION;
676 (void)port_check_reopen(port_ev);
678 if (mpx_fde != NULL) {
679 (void)port_update_event(port_ev, mpx_fde);
682 fde->flags = 0;
683 (void)port_update_event(port_ev, fde);
684 fde->flags = flags;
686 return tevent_common_fd_destructor(fde);
690 add a fd based event
691 return NULL on failure (memory allocation error)
693 static struct tevent_fd *port_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
694 int fd, uint16_t flags,
695 tevent_fd_handler_t handler,
696 void *private_data,
697 const char *handler_name,
698 const char *location)
700 struct port_event_context *port_ev =
701 talloc_get_type_abort(ev->additional_data,
702 struct port_event_context);
703 struct tevent_fd *fde;
705 fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
706 handler, private_data,
707 handler_name, location);
708 if (!fde) {
709 return NULL;
712 talloc_set_destructor(fde, port_event_fd_destructor);
714 if (port_check_reopen(port_ev) != 0) {
715 TALLOC_FREE(fde);
716 return NULL;
719 if (port_update_event(port_ev, fde) != 0) {
720 TALLOC_FREE(fde);
721 return NULL;
724 return fde;
728 set the fd event flags
730 static void port_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
732 struct tevent_context *ev;
733 struct port_event_context *port_ev;
735 if (fde->flags == flags) {
736 return;
739 ev = fde->event_ctx;
740 port_ev = talloc_get_type_abort(ev->additional_data,
741 struct port_event_context);
743 fde->flags = flags;
745 (void)port_check_reopen(port_ev);
746 (void)port_update_event(port_ev, fde);
750 do a single event loop using the events defined in ev
752 static int port_event_loop_once(struct tevent_context *ev, const char *location)
754 struct port_event_context *port_ev = talloc_get_type(ev->additional_data,
755 struct port_event_context);
756 struct timeval tval;
758 if (ev->signal_events &&
759 tevent_common_check_signal(ev)) {
760 return 0;
763 if (ev->threaded_contexts != NULL) {
764 tevent_common_threaded_activate_immediate(ev);
767 if (ev->immediate_events &&
768 tevent_common_loop_immediate(ev)) {
769 return 0;
772 tval = tevent_common_loop_timer_delay(ev);
773 if (tevent_timeval_is_zero(&tval)) {
774 return 0;
777 if (port_check_reopen(port_ev) != 0) {
778 errno = EINVAL;
779 return -1;
781 return port_event_loop(port_ev, &tval);
784 static const struct tevent_ops port_event_ops = {
785 .context_init = port_event_context_init,
786 .add_fd = port_event_add_fd,
787 .set_fd_close_fn = tevent_common_fd_set_close_fn,
788 .get_fd_flags = tevent_common_fd_get_flags,
789 .set_fd_flags = port_event_set_fd_flags,
790 .add_timer = tevent_common_add_timer_v2,
791 .schedule_immediate = tevent_common_schedule_immediate,
792 .add_signal = tevent_common_add_signal,
793 .loop_once = port_event_loop_once,
794 .loop_wait = tevent_common_loop_wait,
797 _PRIVATE_ bool tevent_port_init(void)
799 if (!tevent_register_backend("port", &port_event_ops)) {
800 return false;
802 tevent_set_default_backend("port");
803 return true;