diff --git a/criu/include/util.h b/criu/include/util.h index cf24ed135..c40b0c6f9 100644 --- a/criu/include/util.h +++ b/criu/include/util.h @@ -333,6 +333,14 @@ struct epoll_rfd { * negative error code */ int (*read_event)(struct epoll_rfd *); + + /* + * EPOLLHUP | EPOLLRDHUP notification. The remote side has + * close the connection for rfd->fd. + * @return 0 to resume polling, 1 to stop polling or a + * negative error code + */ + int (*hangup_event)(struct epoll_rfd *); }; extern int epoll_add_rfd(int epfd, struct epoll_rfd *); diff --git a/criu/util.c b/criu/util.c index 3f490eace..eb5ed7336 100644 --- a/criu/util.c +++ b/criu/util.c @@ -1324,7 +1324,7 @@ int epoll_add_rfd(int epfd, struct epoll_rfd *rfd) { struct epoll_event ev; - ev.events = EPOLLIN; + ev.events = EPOLLIN | EPOLLRDHUP; ev.data.ptr = rfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, rfd->fd, &ev) == -1) { pr_perror("epoll_ctl failed"); @@ -1344,6 +1344,24 @@ int epoll_del_rfd(int epfd, struct epoll_rfd *rfd) return 0; } +static int epoll_hangup_event(int epollfd, struct epoll_rfd *rfd) +{ + int ret = 0; + + if (rfd->hangup_event) { + ret = rfd->hangup_event(rfd); + if (ret < 0) + return ret; + } + + if (epoll_del_rfd(epollfd, rfd)) + return -1; + + close_safe(&rfd->fd); + + return ret; +} + int epoll_run_rfds(int epollfd, struct epoll_event *evs, int nr_fds, int timeout) { int ret, i, nr_events; @@ -1360,13 +1378,26 @@ int epoll_run_rfds(int epollfd, struct epoll_event *evs, int nr_fds, int timeout nr_events = ret; for (i = 0; i < nr_events; i++) { struct epoll_rfd *rfd; + uint32_t events; rfd = (struct epoll_rfd *)evs[i].data.ptr; - ret = rfd->read_event(rfd); - if (ret < 0) - goto out; - if (ret > 0) - have_a_break = true; + events = evs[i].events; + + if (events & EPOLLIN) { + ret = rfd->read_event(rfd); + if (ret < 0) + goto out; + if (ret > 0) + have_a_break = true; + } + + if (events & (EPOLLHUP | EPOLLRDHUP)) { + ret = epoll_hangup_event(epollfd, rfd); + if (ret < 0) + goto out; + if (ret > 0) + have_a_break = true; + } } if (have_a_break)