uloop: fix signal unblocking
[project/libubox.git] / uloop-epoll.c
1 /*
2 * uloop - event loop implementation
3 *
4 * Copyright (C) 2010-2016 Felix Fietkau <nbd@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/signalfd.h>
20
21 /**
22 * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17
23 */
24 #ifndef EPOLLRDHUP
25 #define EPOLLRDHUP 0x2000
26 #endif
27
28 static void
29 uloop_signal_fd_cb(struct uloop_fd *fd, unsigned int events)
30 {
31 struct signalfd_siginfo fdsi;
32 int ret;
33
34 retry:
35 ret = read(fd->fd, &fdsi, sizeof(fdsi));
36 if (ret < 0 && errno == EINTR)
37 goto retry;
38
39 if (ret != sizeof(fdsi))
40 return;
41
42 uloop_handle_signal(fdsi.ssi_signo);
43 }
44
45 static bool
46 uloop_setup_signalfd(bool add)
47 {
48 static struct uloop_fd sfd = {
49 .cb = uloop_signal_fd_cb
50 };
51 static sigset_t prev_mask;
52 sigset_t mask;
53
54 if (signal_fd < 0)
55 return false;
56
57 sigemptyset(&mask);
58
59 if (!add) {
60 uloop_fd_delete(&sfd);
61 sigprocmask(SIG_SETMASK, &prev_mask, NULL);
62 } else {
63 sigaddset(&mask, SIGQUIT);
64 sigaddset(&mask, SIGINT);
65 sigaddset(&mask, SIGTERM);
66 sigaddset(&mask, SIGCHLD);
67 sigprocmask(SIG_BLOCK, &mask, &prev_mask);
68
69 sfd.fd = signal_fd;
70 uloop_fd_add(&sfd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
71 }
72
73 if (signalfd(signal_fd, &mask, SFD_NONBLOCK | SFD_CLOEXEC) < 0) {
74 sigprocmask(SIG_BLOCK, &prev_mask, NULL);
75 return false;
76 }
77
78 return true;
79 }
80
81 int uloop_init(void)
82 {
83 sigset_t mask;
84
85 if (poll_fd >= 0)
86 return 0;
87
88 poll_fd = epoll_create(32);
89 if (poll_fd < 0)
90 return -1;
91
92 fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC);
93
94 sigemptyset(&mask);
95 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
96
97 return 0;
98 }
99
100 static int register_poll(struct uloop_fd *fd, unsigned int flags)
101 {
102 struct epoll_event ev;
103 int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
104
105 memset(&ev, 0, sizeof(struct epoll_event));
106
107 if (flags & ULOOP_READ)
108 ev.events |= EPOLLIN | EPOLLRDHUP;
109
110 if (flags & ULOOP_WRITE)
111 ev.events |= EPOLLOUT;
112
113 if (flags & ULOOP_EDGE_TRIGGER)
114 ev.events |= EPOLLET;
115
116 ev.data.fd = fd->fd;
117 ev.data.ptr = fd;
118 fd->flags = flags;
119
120 return epoll_ctl(poll_fd, op, fd->fd, &ev);
121 }
122
123 static struct epoll_event events[ULOOP_MAX_EVENTS];
124
125 static int __uloop_fd_delete(struct uloop_fd *sock)
126 {
127 sock->flags = 0;
128 return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0);
129 }
130
131 static int uloop_fetch_events(int timeout)
132 {
133 int n, nfds;
134
135 nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout);
136 for (n = 0; n < nfds; ++n) {
137 struct uloop_fd_event *cur = &cur_fds[n];
138 struct uloop_fd *u = events[n].data.ptr;
139 unsigned int ev = 0;
140
141 cur->fd = u;
142 if (!u)
143 continue;
144
145 if (events[n].events & (EPOLLERR|EPOLLHUP)) {
146 u->error = true;
147 if (!(u->flags & ULOOP_ERROR_CB))
148 uloop_fd_delete(u);
149 }
150
151 if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) {
152 cur->fd = NULL;
153 continue;
154 }
155
156 if(events[n].events & EPOLLRDHUP)
157 u->eof = true;
158
159 if(events[n].events & EPOLLIN)
160 ev |= ULOOP_READ;
161
162 if(events[n].events & EPOLLOUT)
163 ev |= ULOOP_WRITE;
164
165 cur->events = ev;
166 }
167
168 return nfds;
169 }