/* change the epoll event to the given fd_event */ static void epoll_mod_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; ZERO_STRUCT(event); event.events = epoll_map_flags(fde->flags); event.data.ptr = fde; if (epoll_ctl(epoll_ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) { epoll_panic(epoll_ev, "EPOLL_CTL_MOD failed"); } /* only if we want to read we want to tell the event handler about errors */ if (fde->flags & TEVENT_FD_READ) { fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR; } }
/* 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 epoll */ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval *tvalp) { int ret, i; #define MAXEVENTS 1 struct epoll_event events[MAXEVENTS]; int timeout = -1; if (epoll_ev->epoll_fd == -1) return -1; if (tvalp) { /* it's better to trigger timed events a bit later than to early */ timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000); } if (epoll_ev->ev->signal_events && tevent_common_check_signal(epoll_ev->ev)) { return 0; } ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout); if (ret == -1 && errno == EINTR && epoll_ev->ev->signal_events) { if (tevent_common_check_signal(epoll_ev->ev)) { return 0; } } if (ret == -1 && errno != EINTR) { epoll_panic(epoll_ev, "epoll_wait() failed"); return -1; } if (ret == 0 && tvalp) { /* we don't care about a possible delay here */ tevent_common_loop_timer_delay(epoll_ev->ev); return 0; } for (i=0;i<ret;i++) { struct tevent_fd *fde = talloc_get_type(events[i].data.ptr, struct tevent_fd); uint16_t flags = 0; if (fde == NULL) { epoll_panic(epoll_ev, "epoll_wait() gave bad data"); return -1; } if (events[i].events & (EPOLLHUP|EPOLLERR)) { fde->additional_flags |= EPOLL_ADDITIONAL_FD_FLAG_GOT_ERROR; /* * if we only wait for TEVENT_FD_WRITE, we should not tell the * event handler about it, and remove the epoll_event, * as we only report errors when waiting for read events, * to match the select() behavior */ if (!(fde->additional_flags & EPOLL_ADDITIONAL_FD_FLAG_REPORT_ERROR)) { epoll_del_event(epoll_ev, fde); continue; } flags |= TEVENT_FD_READ; } if (events[i].events & EPOLLIN) flags |= TEVENT_FD_READ; if (events[i].events & EPOLLOUT) flags |= TEVENT_FD_WRITE; if (flags) { fde->handler(epoll_ev->ev, fde, flags, fde->private_data); break; } } return 0; }