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
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/>.
28 #include "system/filesys.h"
29 #include "system/select.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
;
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 */
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
)
66 if (flags
& TEVENT_FD_READ
) ret
|= (POLLIN
| POLLERR
| POLLHUP
);
67 if (flags
& TEVENT_FD_WRITE
) ret
|= (POLLOUT
| POLLERR
| POLLHUP
);
74 static int port_ctx_destructor(struct port_event_context
*port_ev
)
76 close(port_ev
->port_fd
);
77 port_ev
->port_fd
= -1;
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");
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
);
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
));
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
,
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;
138 val
= talloc_zero(port_ev
, struct port_associate_vals
);
143 val
->port_ev
= port_ev
;
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
);
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
,
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 if (val
->associated_event
) {
180 int ret
= port_associate(port_ev
->port_fd
,
182 (uintptr_t)val
->fde
->fd
,
188 val
->associated_event
= true;
193 static int port_update_event(struct port_event_context
*port_ev
, struct tevent_fd
*fde
);
196 Reopen the port handle when our pid changes.
198 static int port_check_reopen(struct port_event_context
*port_ev
)
200 struct tevent_fd
*fde
;
202 if (port_ev
->pid
== getpid()) {
206 close(port_ev
->port_fd
);
207 port_ev
->port_fd
= port_create();
208 if (port_ev
->port_fd
== -1) {
209 tevent_debug(port_ev
->ev
, TEVENT_DEBUG_FATAL
,
210 "port_create() failed");
214 if (!ev_set_close_on_exec(port_ev
->port_fd
)) {
215 tevent_debug(port_ev
->ev
, TEVENT_DEBUG_WARNING
,
216 "Failed to set close-on-exec, file descriptor may be leaked to children.\n");
219 port_ev
->pid
= getpid();
220 for (fde
=port_ev
->ev
->fd_events
;fde
;fde
=fde
->next
) {
221 fde
->additional_flags
&= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
222 if (port_update_event(port_ev
, fde
) != 0) {
230 * Solaris ports cannot add the same file descriptor twice, once
231 * with read, once with write which is allowed by the tevent backend.
232 * Multiplex the existing fde, flag it as such so we can search for the
233 * correct fde on event triggering.
236 static void port_setup_multiplex_fd(struct port_event_context
*port_ev
,
237 struct tevent_fd
*add_fde
,
238 struct tevent_fd
*mpx_fde
)
241 * Make each fde->additional_data pointers point at each other
242 * so we can look them up from each other. They are now paired.
244 mpx_fde
->additional_data
= add_fde
;
245 add_fde
->additional_data
= mpx_fde
;
247 /* Now flag both fde's as being multiplexed. */
248 mpx_fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_HAS_MPX
;
249 add_fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_HAS_MPX
;
251 /* We need to keep the GOT_ERROR flag. */
252 if (mpx_fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_GOT_ERROR
) {
253 add_fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_GOT_ERROR
;
258 Add the port event to the given fd_event,
259 Or modify an existing event.
262 static int port_add_event(struct port_event_context
*port_ev
, struct tevent_fd
*fde
)
264 int flags
= port_map_flags(fde
->flags
);
265 struct tevent_fd
*mpx_fde
= NULL
;
267 fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
268 fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
270 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
272 * This is already a multiplexed fde, we need to include both
273 * flags in the modified event.
275 mpx_fde
= talloc_get_type_abort(fde
->additional_data
,
278 mpx_fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
279 mpx_fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
281 flags
|= port_map_flags(mpx_fde
->flags
);
284 * Not (yet) a multiplexed event. See if there
285 * is already an event with the same fd.
287 for (mpx_fde
= port_ev
->ev
->fd_events
; mpx_fde
; mpx_fde
= mpx_fde
->next
) {
288 if (mpx_fde
->fd
!= fde
->fd
) {
291 if (mpx_fde
== fde
) {
298 if (mpx_fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
299 /* Logic error. Can't have more then 2 multiplexed fde's. */
300 tevent_debug(port_ev
->ev
, TEVENT_DEBUG_FATAL
,
301 "multiplex fde for fd[%d] is already multiplexed\n",
305 flags
|= port_map_flags(mpx_fde
->flags
);
309 if (!store_port_association(port_ev
,
312 tevent_debug(port_ev
->ev
, TEVENT_DEBUG_FATAL
,
313 "store_port_association failed for fd[%d]\n",
318 /* Note we have an association now. */
319 fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
320 /* Only if we want to read do we tell the event handler about errors. */
321 if (fde
->flags
& TEVENT_FD_READ
) {
322 fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
324 if (mpx_fde
== NULL
) {
327 /* Set up the multiplex pointer. Does no harm if already multiplexed. */
328 port_setup_multiplex_fd(port_ev
,
332 mpx_fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
333 /* Only if we want to read do we tell the event handler about errors. */
334 if (mpx_fde
->flags
& TEVENT_FD_READ
) {
335 mpx_fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
342 Delete the port association for the given fd_event.
345 static void port_del_event(struct port_event_context
*port_ev
, struct tevent_fd
*fde
)
347 struct tevent_fd
*mpx_fde
= NULL
;
349 fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
350 fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
352 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
354 * This is a multiplexed fde, we need to remove
357 mpx_fde
= talloc_get_type_abort(fde
->additional_data
,
360 mpx_fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
361 mpx_fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
;
362 mpx_fde
->additional_data
= NULL
;
364 fde
->additional_data
= NULL
;
366 delete_port_association(port_ev
, fde
);
370 Add or remove the port event from the given fd_event
372 static int port_update_event(struct port_event_context
*port_ev
, struct tevent_fd
*fde
)
374 bool got_error
= (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_GOT_ERROR
);
375 bool want_read
= (fde
->flags
& TEVENT_FD_READ
);
376 bool want_write
= (fde
->flags
& TEVENT_FD_WRITE
);
377 struct tevent_fd
*mpx_fde
= NULL
;
379 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
381 * work out what the multiplexed fde wants.
383 mpx_fde
= talloc_get_type_abort(fde
->additional_data
,
385 if (mpx_fde
->flags
& TEVENT_FD_READ
) {
388 if (mpx_fde
->flags
& TEVENT_FD_WRITE
) {
393 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
) {
394 /* There's already an association. */
395 if (want_read
|| (want_write
&& !got_error
)) {
396 return port_add_event(port_ev
, fde
);
399 * If we want to match the select behavior, we need to remove the port event
400 * when the caller isn't interested in events.
402 port_del_event(port_ev
, fde
);
406 /* There's no port event attached to the fde. */
407 if (want_read
|| (want_write
&& !got_error
)) {
408 return port_add_event(port_ev
, fde
);
414 Cope with port_get returning EPOLLHP|EPOLLERR on an association.
415 Return true if there's nothing else to do, false if this event
416 needs further handling.
419 static bool port_handle_hup_or_err(struct port_event_context
*port_ev
,
420 struct tevent_fd
*fde
)
426 fde
->additional_flags
|= PORT_ADDITIONAL_FD_FLAG_GOT_ERROR
;
428 * If we only wait for TEVENT_FD_WRITE, we should not tell the
429 * event handler about it, and remove the port association,
430 * as we only report error when waiting for read events,
431 * to match the select() behavior.
433 if (!(fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_REPORT_ERROR
)) {
435 * Do the same as the poll backend and
436 * remove the writable flag.
438 fde
->flags
&= ~TEVENT_FD_WRITE
;
441 /* This has TEVENT_FD_READ set, we're not finished. */
446 Event loop handling using Solaris ports.
448 static int port_event_loop(struct port_event_context
*port_ev
, struct timeval
*tvalp
)
452 port_event_t events
[MAXEVENTS
];
454 uint_t max_events
= MAXEVENTS
;
458 struct tevent_context
*ev
= port_ev
->ev
;
461 ts
.tv_sec
= tvalp
->tv_sec
;
462 ts
.tv_nsec
= tvalp
->tv_usec
* 1000;
465 if (port_ev
->ev
->signal_events
&&
466 tevent_common_check_signal(ev
)) {
471 * Solaris triggers sending the event to the port
472 * at the time the port association is done. Postpone
473 * associating fd's until just before we get the events,
474 * otherwise we can deadlock.
477 if (associate_all_events(port_ev
) != 0) {
481 tevent_trace_point_callback(ev
, TEVENT_TRACE_BEFORE_WAIT
);
482 ret
= port_getn(port_ev
->port_fd
, events
, max_events
, &nget
, &ts
);
484 tevent_trace_point_callback(ev
, TEVENT_TRACE_AFTER_WAIT
);
486 if (ret
== -1 && port_errno
== EINTR
) {
487 if (ev
->signal_events
) {
488 tevent_common_check_signal(ev
);
491 * If no signal handlers we got an unsolicited
492 * signal wakeup. This can happen with epoll
493 * too. Just return and ignore.
498 if (ret
== -1 && port_errno
== ETIME
&& tvalp
) {
499 /* we don't care about a possible delay here */
500 tevent_common_loop_timer_delay(ev
);
505 tevent_debug(ev
, TEVENT_DEBUG_ERROR
,
506 "port_get failed (%s)\n",
511 for (i
= 0; i
< nget
; i
++) {
512 struct tevent_fd
*mpx_fde
= NULL
;
513 struct tevent_fd
*fde
= NULL
;
515 struct port_associate_vals
*val
= talloc_get_type(events
[i
].portev_user
,
516 struct port_associate_vals
);
518 tevent_debug(ev
, TEVENT_DEBUG_ERROR
,
519 "port_getn() gave bad data");
523 /* Mark this event as needing to be re-associated. */
524 val
->associated_event
= false;
528 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
530 * Save off the multiplexed event in case we need
531 * to use it to call the handler function.
533 mpx_fde
= talloc_get_type_abort(fde
->additional_data
,
537 if (events
[i
].portev_events
& (POLLHUP
|POLLERR
)) {
538 bool handled_fde
= port_handle_hup_or_err(port_ev
, fde
);
539 bool handled_mpx
= port_handle_hup_or_err(port_ev
, mpx_fde
);
541 if (handled_fde
&& handled_mpx
) {
542 return port_update_event(port_ev
, fde
);
547 * If the mpx event was the one that needs
548 * further handling, it's the TEVENT_FD_READ
549 * event so switch over and call that handler.
554 flags
|= TEVENT_FD_READ
;
557 if (events
[i
].portev_events
& POLLIN
) {
558 flags
|= TEVENT_FD_READ
;
560 if (events
[i
].portev_events
& POLLOUT
) {
561 flags
|= TEVENT_FD_WRITE
;
564 if (flags
& TEVENT_FD_WRITE
) {
565 if (fde
->flags
& TEVENT_FD_WRITE
) {
568 if (mpx_fde
&& (mpx_fde
->flags
& TEVENT_FD_WRITE
)) {
574 /* Ensure we got the right fde. */
575 if ((flags
& fde
->flags
) == 0) {
583 * Make sure we only pass the flags
584 * the handler is expecting.
588 fde
->handler(ev
, fde
, flags
, fde
->private_data
);
598 create a port_event_context structure.
600 static int port_event_context_init(struct tevent_context
*ev
)
603 struct port_event_context
*port_ev
;
606 * We might be called during tevent_re_initialise()
607 * which means we need to free our old additional_data.
609 TALLOC_FREE(ev
->additional_data
);
611 port_ev
= talloc_zero(ev
, struct port_event_context
);
616 port_ev
->port_fd
= -1;
617 port_ev
->pid
= (pid_t
)-1;
619 ret
= port_init_ctx(port_ev
);
621 talloc_free(port_ev
);
625 ev
->additional_data
= port_ev
;
632 static int port_event_fd_destructor(struct tevent_fd
*fde
)
634 struct tevent_context
*ev
= fde
->event_ctx
;
635 struct port_event_context
*port_ev
= NULL
;
636 struct tevent_fd
*mpx_fde
= NULL
;
637 int flags
= (int)fde
->flags
;
640 return tevent_common_fd_destructor(fde
);
643 port_ev
= talloc_get_type_abort(ev
->additional_data
,
644 struct port_event_context
);
646 DLIST_REMOVE(ev
->fd_events
, fde
);
648 if (fde
->additional_flags
& PORT_ADDITIONAL_FD_FLAG_HAS_MPX
) {
649 mpx_fde
= talloc_get_type_abort(fde
->additional_data
,
652 fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX
;
653 mpx_fde
->additional_flags
&= ~PORT_ADDITIONAL_FD_FLAG_HAS_MPX
;
655 fde
->additional_data
= NULL
;
656 mpx_fde
->additional_data
= NULL
;
658 fde
->additional_flags
&= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION
;
661 (void)port_check_reopen(port_ev
);
663 if (mpx_fde
!= NULL
) {
664 (void)port_update_event(port_ev
, mpx_fde
);
668 (void)port_update_event(port_ev
, fde
);
671 return tevent_common_fd_destructor(fde
);
676 return NULL on failure (memory allocation error)
678 static struct tevent_fd
*port_event_add_fd(struct tevent_context
*ev
, TALLOC_CTX
*mem_ctx
,
679 int fd
, uint16_t flags
,
680 tevent_fd_handler_t handler
,
682 const char *handler_name
,
683 const char *location
)
685 struct port_event_context
*port_ev
=
686 talloc_get_type_abort(ev
->additional_data
,
687 struct port_event_context
);
688 struct tevent_fd
*fde
;
690 fde
= tevent_common_add_fd(ev
, mem_ctx
, fd
, flags
,
691 handler
, private_data
,
692 handler_name
, location
);
697 talloc_set_destructor(fde
, port_event_fd_destructor
);
699 if (port_check_reopen(port_ev
) != 0) {
704 if (port_update_event(port_ev
, fde
) != 0) {
713 set the fd event flags
715 static void port_event_set_fd_flags(struct tevent_fd
*fde
, uint16_t flags
)
717 struct tevent_context
*ev
;
718 struct port_event_context
*port_ev
;
720 if (fde
->flags
== flags
) {
725 port_ev
= talloc_get_type_abort(ev
->additional_data
,
726 struct port_event_context
);
730 (void)port_check_reopen(port_ev
);
731 (void)port_update_event(port_ev
, fde
);
735 do a single event loop using the events defined in ev
737 static int port_event_loop_once(struct tevent_context
*ev
, const char *location
)
739 struct port_event_context
*port_ev
= talloc_get_type(ev
->additional_data
,
740 struct port_event_context
);
743 if (ev
->signal_events
&&
744 tevent_common_check_signal(ev
)) {
748 if (ev
->immediate_events
&&
749 tevent_common_loop_immediate(ev
)) {
753 tval
= tevent_common_loop_timer_delay(ev
);
754 if (tevent_timeval_is_zero(&tval
)) {
758 if (port_check_reopen(port_ev
) != 0) {
762 return port_event_loop(port_ev
, &tval
);
765 static const struct tevent_ops port_event_ops
= {
766 .context_init
= port_event_context_init
,
767 .add_fd
= port_event_add_fd
,
768 .set_fd_close_fn
= tevent_common_fd_set_close_fn
,
769 .get_fd_flags
= tevent_common_fd_get_flags
,
770 .set_fd_flags
= port_event_set_fd_flags
,
771 .add_timer
= tevent_common_add_timer_v2
,
772 .schedule_immediate
= tevent_common_schedule_immediate
,
773 .add_signal
= tevent_common_add_signal
,
774 .loop_once
= port_event_loop_once
,
775 .loop_wait
= tevent_common_loop_wait
,
778 _PRIVATE_
bool tevent_port_init(void)
780 if (!tevent_register_backend("port", &port_event_ops
)) {
783 tevent_set_default_backend("port");