uloop: try to use signalfd for signal handling if available
[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_BLOCK, &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 }