/* reopen the epoll handle when our pid changes see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an demonstration of why this is needed */ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) { struct tevent_fd *fde; if (epoll_ev->pid == getpid()) { return; } close(epoll_ev->epoll_fd); epoll_ev->epoll_fd = epoll_create(64); if (epoll_ev->epoll_fd == -1) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "Failed to recreate epoll handle after fork\n"); return; } if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING, "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); } epoll_ev->pid = getpid(); for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) { epoll_add_event(epoll_ev, fde); } }
/* Reopen the port handle when our pid changes. */ static int port_check_reopen(struct port_event_context *port_ev) { struct tevent_fd *fde; if (port_ev->pid == getpid()) { return 0; } close(port_ev->port_fd); port_ev->port_fd = port_create(); if (port_ev->port_fd == -1) { tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL, "port_create() failed"); return -1; } if (!ev_set_close_on_exec(port_ev->port_fd)) { tevent_debug(port_ev->ev, TEVENT_DEBUG_WARNING, "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); } port_ev->pid = getpid(); for (fde=port_ev->ev->fd_events;fde;fde=fde->next) { fde->additional_flags &= PORT_ADDITIONAL_FD_FLAG_HAS_ASSOCIATION; if (port_update_event(port_ev, fde) != 0) { return -1; } } return 0; }
/* called when a epoll call fails, and we should fallback to using select */ static void epoll_fallback_to_select(struct std_event_context *std_ev, const char *reason) { tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, "%s (%s) - falling back to select()\n", reason, strerror(errno)); close(std_ev->epoll_fd); std_ev->epoll_fd = -1; talloc_set_destructor(std_ev, NULL); }
/* Init the port fd */ static int port_init_ctx(struct port_event_context *port_ev) { port_ev->port_fd = port_create(); if (port_ev->port_fd == -1) { tevent_debug(port_ev->ev, TEVENT_DEBUG_FATAL, "Failed to create port handle.\n"); return -1; } if (!ev_set_close_on_exec(port_ev->port_fd)) { tevent_debug(port_ev->ev, TEVENT_DEBUG_WARNING, "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); } port_ev->pid = getpid(); talloc_set_destructor(port_ev, port_ctx_destructor); return 0; }
/* init the epoll fd */ static int epoll_init_ctx(struct epoll_event_context *epoll_ev) { epoll_ev->epoll_fd = epoll_create(64); if (epoll_ev->epoll_fd == -1) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "Failed to create epoll handle.\n"); return -1; } if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING, "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); } epoll_ev->pid = getpid(); talloc_set_destructor(epoll_ev, epoll_ctx_destructor); return 0; }
/* called when a epoll call fails */ static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason, bool replay) { struct tevent_context *ev = epoll_ev->ev; bool (*panic_fallback)(struct tevent_context *ev, bool replay); panic_fallback = epoll_ev->panic_fallback; if (epoll_ev->panic_state != NULL) { *epoll_ev->panic_state = true; } if (epoll_ev->panic_force_replay) { replay = true; } TALLOC_FREE(ev->additional_data); if (panic_fallback == NULL) { tevent_debug(ev, TEVENT_DEBUG_FATAL, "%s (%s) replay[%u] - calling abort()\n", reason, strerror(errno), (unsigned)replay); abort(); } tevent_debug(ev, TEVENT_DEBUG_ERROR, "%s (%s) replay[%u] - calling panic_fallback\n", reason, strerror(errno), (unsigned)replay); if (!panic_fallback(ev, replay)) { /* Fallback failed. */ tevent_debug(ev, TEVENT_DEBUG_FATAL, "%s (%s) replay[%u] - calling abort()\n", reason, strerror(errno), (unsigned)replay); abort(); } }
/* reopen the epoll handle when our pid changes see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an demonstration of why this is needed */ static void epoll_check_reopen(struct std_event_context *std_ev) { struct tevent_fd *fde; if (std_ev->pid == getpid()) { return; } close(std_ev->epoll_fd); std_ev->epoll_fd = epoll_create(64); if (std_ev->epoll_fd == -1) { tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, "Failed to recreate epoll handle after fork\n"); return; } std_ev->pid = getpid(); for (fde=std_ev->ev->fd_events;fde;fde=fde->next) { epoll_add_event(std_ev, fde); } }
/* delete the epoll event for given fd_event */ static void epoll_del_event(struct epoll_event_context *epoll_ev, struct tevent_fd *fde) { struct epoll_event event; if (epoll_ev->epoll_fd == -1) return; fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; /* if there's no epoll_event, we don't need to delete it */ if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) return; ZERO_STRUCT(event); event.events = epoll_map_flags(fde->flags); event.data.ptr = fde; if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event) != 0) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "epoll_del_event failed! probable early close bug (%s)\n", strerror(errno)); } fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; }
/* reopen the epoll handle when our pid changes see http://junkcode.samba.org/ftp/unpacked/junkcode/epoll_fork.c for an demonstration of why this is needed */ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) { struct tevent_fd *fde; bool *caller_panic_state = epoll_ev->panic_state; bool panic_triggered = false; if (epoll_ev->pid == getpid()) { return; } close(epoll_ev->epoll_fd); epoll_ev->epoll_fd = epoll_create(64); if (epoll_ev->epoll_fd == -1) { epoll_panic(epoll_ev, "epoll_create() failed", false); return; } if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING, "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); } epoll_ev->pid = getpid(); epoll_ev->panic_state = &panic_triggered; for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) { fde->additional_flags &= ~EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT; epoll_update_event(epoll_ev, fde); if (panic_triggered) { if (caller_panic_state != NULL) { *caller_panic_state = true; } return; } } epoll_ev->panic_state = NULL; }
/* event loop handling using poll() */ static int poll_event_loop_poll(struct tevent_context *ev, struct timeval *tvalp) { struct poll_event_context *poll_ev = talloc_get_type_abort( ev->additional_data, struct poll_event_context); int pollrtn; int timeout = -1; int poll_errno; struct tevent_fd *fde = NULL; unsigned i; if (ev->signal_events && tevent_common_check_signal(ev)) { return 0; } if (tvalp != NULL) { timeout = tvalp->tv_sec * 1000; timeout += (tvalp->tv_usec + 999) / 1000; } poll_event_drain_signal_fd(poll_ev); if (!poll_event_setup_fresh(ev, poll_ev)) { return -1; } tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_BEFORE_WAIT); pollrtn = poll(poll_ev->fds, poll_ev->num_fds, timeout); poll_errno = errno; tevent_trace_point_callback(poll_ev->ev, TEVENT_TRACE_AFTER_WAIT); if (pollrtn == -1 && poll_errno == EINTR && ev->signal_events) { tevent_common_check_signal(ev); return 0; } if (pollrtn == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(ev); return 0; } if (pollrtn <= 0) { /* * No fd's ready */ return 0; } /* at least one file descriptor is ready - check which ones and call the handler, being careful to allow the handler to remove itself when called */ for (fde = ev->fd_events; fde; fde = fde->next) { unsigned idx = fde->additional_flags; struct pollfd *pfd; uint16_t flags = 0; if (idx == UINT64_MAX) { continue; } pfd = &poll_ev->fds[idx]; if (pfd->revents & POLLNVAL) { /* * the socket is dead! this should never * happen as the socket should have first been * made readable and that should have removed * the event, so this must be a bug. * * We ignore it here to match the epoll * behavior. */ tevent_debug(ev, TEVENT_DEBUG_ERROR, "POLLNVAL on fde[%p] fd[%d] - disabling\n", fde, pfd->fd); poll_ev->fdes[idx] = NULL; poll_ev->deleted = true; DLIST_REMOVE(ev->fd_events, fde); fde->event_ctx = NULL; continue; } if (pfd->revents & (POLLHUP|POLLERR)) { /* If we only wait for TEVENT_FD_WRITE, we should not tell the event handler about it, and remove the writable flag, as we only report errors when waiting for read events to match the select behavior. */ if (!(fde->flags & TEVENT_FD_READ)) { TEVENT_FD_NOT_WRITEABLE(fde); continue; } flags |= TEVENT_FD_READ; } if (pfd->revents & POLLIN) { flags |= TEVENT_FD_READ; } if (pfd->revents & POLLOUT) { flags |= TEVENT_FD_WRITE; } /* * Note that fde->flags could be changed when using * the poll_mt backend together with threads, * that why we need to check pfd->revents and fde->flags */ flags &= fde->flags; if (flags != 0) { DLIST_DEMOTE(ev->fd_events, fde, struct tevent_fd); fde->handler(ev, fde, flags, fde->private_data); return 0; } }
static int epoll_add_multiplex_fd(struct epoll_event_context *epoll_ev, struct tevent_fd *add_fde) { struct epoll_event event; struct tevent_fd *mpx_fde; int ret; /* Find the existing fde that caused the EEXIST error. */ for (mpx_fde = epoll_ev->ev->fd_events; mpx_fde; mpx_fde = mpx_fde->next) { if (mpx_fde->fd != add_fde->fd) { continue; } if (mpx_fde == add_fde) { continue; } break; } if (mpx_fde == NULL) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "can't find multiplex fde for fd[%d]", add_fde->fd); return -1; } if (mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX) { /* Logic error. Can't have more than 2 multiplexed fde's. */ tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "multiplex fde for fd[%d] is already multiplexed\n", mpx_fde->fd); return -1; } /* * The multiplex fde must have the same fd, and also * already have an epoll event attached. */ if (!(mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT)) { /* Logic error. Can't have more than 2 multiplexed fde's. */ tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "multiplex fde for fd[%d] has no event\n", mpx_fde->fd); return -1; } /* Modify the mpx_fde to add in the new flags. */ ZERO_STRUCT(event); event.events = epoll_map_flags(mpx_fde->flags); event.events |= epoll_map_flags(add_fde->flags); event.data.ptr = mpx_fde; ret = epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_MOD, mpx_fde->fd, &event); if (ret != 0 && errno == EBADF) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_ERROR, "EPOLL_CTL_MOD EBADF for " "add_fde[%p] mpx_fde[%p] fd[%d] - disabling\n", add_fde, mpx_fde, add_fde->fd); DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde); mpx_fde->event_ctx = NULL; DLIST_REMOVE(epoll_ev->ev->fd_events, add_fde); add_fde->event_ctx = NULL; return 0; } else if (ret != 0) { return ret; } /* * Make each fde->additional_data pointers point at each other * so we can look them up from each other. They are now paired. */ mpx_fde->additional_data = (struct tevent_fd *)add_fde; add_fde->additional_data = (struct tevent_fd *)mpx_fde; /* Now flag both fde's as being multiplexed. */ mpx_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX; add_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_HAS_MPX; /* we need to keep the GOT_ERROR flag */ if (mpx_fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR) { add_fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR; } return 0; }
/* event loop handling using select() */ static int std_event_loop_select(struct std_event_context *std_ev, struct timeval *tvalp) { fd_set r_fds, w_fds; struct tevent_fd *fde; int selrtn; /* we maybe need to recalculate the maxfd */ if (std_ev->maxfd == EVENT_INVALID_MAXFD) { calc_maxfd(std_ev); } FD_ZERO(&r_fds); FD_ZERO(&w_fds); /* setup any fd events */ for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { if (fde->fd < 0 || fde->fd >= FD_SETSIZE) { std_ev->exit_code = EBADF; return -1; } if (fde->flags & TEVENT_FD_READ) { FD_SET(fde->fd, &r_fds); } if (fde->flags & TEVENT_FD_WRITE) { FD_SET(fde->fd, &w_fds); } } if (std_ev->ev->signal_events && tevent_common_check_signal(std_ev->ev)) { return 0; } selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); if (selrtn == -1 && errno == EINTR && std_ev->ev->signal_events) { tevent_common_check_signal(std_ev->ev); return 0; } if (selrtn == -1 && errno == EBADF) { /* the socket is dead! this should never happen as the socket should have first been made readable and that should have removed the event, so this must be a bug. This is a fatal error. */ tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, "ERROR: EBADF on std_event_loop_once\n"); std_ev->exit_code = EBADF; return -1; } if (selrtn == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(std_ev->ev); return 0; } if (selrtn > 0) { /* at least one file descriptor is ready - check which ones and call the handler, being careful to allow the handler to remove itself when called */ for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { uint16_t flags = 0; if (FD_ISSET(fde->fd, &r_fds)) flags |= TEVENT_FD_READ; if (FD_ISSET(fde->fd, &w_fds)) flags |= TEVENT_FD_WRITE; if (flags) { fde->handler(std_ev->ev, fde, flags, fde->private_data); break; } } } return 0; }
/* called when a epoll call fails */ static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason) { tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, "%s (%s) - calling abort()\n", reason, strerror(errno)); abort(); }