]> jfr.im git - irc/rqf/shadowircd.git/blob - libratbox/src/epoll.c
1f99568309dcb5ada9fd661ba4379f7f502b93a0
[irc/rqf/shadowircd.git] / libratbox / src / epoll.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * epoll.c: Linux epoll compatible network routines.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2001 Adrian Chadd <adrian@creative.net.au>
8 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * Copyright (C) 2002 Aaron Sethman <androsyn@ratbox.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
24 * USA
25 *
26 * $Id: epoll.c 25675 2008-07-06 04:13:05Z androsyn $
27 */
28 #define _GNU_SOURCE 1
29
30 #include <libratbox_config.h>
31 #include <ratbox_lib.h>
32 #include <commio-int.h>
33 #include <event-int.h>
34
35 #if defined(HAVE_EPOLL_CTL) && (HAVE_SYS_EPOLL_H)
36 #define USING_EPOLL
37 #include <fcntl.h>
38 #include <sys/epoll.h>
39
40 #if defined(HAVE_SIGNALFD) && (HAVE_SYS_SIGNALFD_H) && (USE_TIMER_CREATE) && (HAVE_SYS_UIO_H)
41 #include <signal.h>
42 #include <sys/signalfd.h>
43 #include <sys/uio.h>
44 #define EPOLL_SCHED_EVENT 1
45 #endif
46
47 #define RTSIGNAL SIGRTMIN
48 struct epoll_info
49 {
50 int ep;
51 struct epoll_event *pfd;
52 int pfd_size;
53 };
54
55 static struct epoll_info *ep_info;
56 static int can_do_event;
57
58 //static void setup_signalfd(void);
59
60 /*
61 * rb_init_netio
62 *
63 * This is a needed exported function which will be called to initialise
64 * the network loop code.
65 */
66 int
67 rb_init_netio_epoll(void)
68 {
69 can_do_event = 0; /* shut up gcc */
70 ep_info = rb_malloc(sizeof(struct epoll_info));
71 ep_info->pfd_size = getdtablesize();
72 ep_info->ep = epoll_create(ep_info->pfd_size);
73 if(ep_info->ep < 0)
74 {
75 return -1;
76 }
77 rb_open(ep_info->ep, RB_FD_UNKNOWN, "epoll file descriptor");
78 ep_info->pfd = rb_malloc(sizeof(struct epoll_event) * ep_info->pfd_size);
79
80 return 0;
81 }
82
83 int
84 rb_setup_fd_epoll(rb_fde_t * F)
85 {
86 return 0;
87 }
88
89
90 /*
91 * rb_setselect
92 *
93 * This is a needed exported function which will be called to register
94 * and deregister interest in a pending IO state for a given FD.
95 */
96 void
97 rb_setselect_epoll(rb_fde_t * F, unsigned int type, PF * handler, void *client_data)
98 {
99 struct epoll_event ep_event;
100 int old_flags = F->pflags;
101 int op = -1;
102
103 lrb_assert(IsFDOpen(F));
104
105 /* Update the list, even though we're not using it .. */
106 if(type & RB_SELECT_READ)
107 {
108 if(handler != NULL)
109 F->pflags |= EPOLLIN;
110 else
111 F->pflags &= ~EPOLLIN;
112 F->read_handler = handler;
113 F->read_data = client_data;
114 }
115
116 if(type & RB_SELECT_WRITE)
117 {
118 if(handler != NULL)
119 F->pflags |= EPOLLOUT;
120 else
121 F->pflags &= ~EPOLLOUT;
122 F->write_handler = handler;
123 F->write_data = client_data;
124 }
125
126 if(old_flags == 0 && F->pflags == 0)
127 return;
128 else if(F->pflags <= 0)
129 op = EPOLL_CTL_DEL;
130 else if(old_flags == 0 && F->pflags > 0)
131 op = EPOLL_CTL_ADD;
132 else if(F->pflags != old_flags)
133 op = EPOLL_CTL_MOD;
134
135 if(op == -1)
136 return;
137
138 ep_event.events = F->pflags;
139 ep_event.data.ptr = F;
140
141 if(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD)
142 ep_event.events |= EPOLLET;
143
144 if(epoll_ctl(ep_info->ep, op, F->fd, &ep_event) != 0)
145 {
146 rb_lib_log("rb_setselect_epoll(): epoll_ctl failed: %s", strerror(errno));
147 abort();
148 }
149
150
151 }
152
153 /*
154 * rb_select
155 *
156 * Called to do the new-style IO, courtesy of squid (like most of this
157 * new IO code). This routine handles the stuff we've hidden in
158 * rb_setselect and fd_table[] and calls callbacks for IO ready
159 * events.
160 */
161
162 int
163 rb_select_epoll(long delay)
164 {
165 int num, i, flags, old_flags, op;
166 struct epoll_event ep_event;
167 int o_errno;
168 void *data;
169
170 num = epoll_wait(ep_info->ep, ep_info->pfd, ep_info->pfd_size, delay);
171
172 /* save errno as rb_set_time() will likely clobber it */
173 o_errno = errno;
174 rb_set_time();
175 errno = o_errno;
176
177 if(num < 0 && !rb_ignore_errno(o_errno))
178 return RB_ERROR;
179
180 if(num <= 0)
181 return RB_OK;
182
183 for (i = 0; i < num; i++)
184 {
185 PF *hdl;
186 rb_fde_t *F = ep_info->pfd[i].data.ptr;
187 old_flags = F->pflags;
188 if(ep_info->pfd[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR))
189 {
190 hdl = F->read_handler;
191 data = F->read_data;
192 F->read_handler = NULL;
193 F->read_data = NULL;
194 if(hdl)
195 {
196 hdl(F, data);
197 }
198 }
199
200 if(!IsFDOpen(F))
201 continue;
202 if(ep_info->pfd[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR))
203 {
204 hdl = F->write_handler;
205 data = F->write_data;
206 F->write_handler = NULL;
207 F->write_data = NULL;
208
209 if(hdl)
210 {
211 hdl(F, data);
212 }
213 }
214
215 if(!IsFDOpen(F))
216 continue;
217
218 flags = 0;
219
220 if(F->read_handler != NULL)
221 flags |= EPOLLIN;
222 if(F->write_handler != NULL)
223 flags |= EPOLLOUT;
224
225 if(old_flags != flags)
226 {
227 if(flags == 0)
228 op = EPOLL_CTL_DEL;
229 else
230 op = EPOLL_CTL_MOD;
231 F->pflags = ep_event.events = flags;
232 ep_event.data.ptr = F;
233 if(op == EPOLL_CTL_MOD || op == EPOLL_CTL_ADD)
234 ep_event.events |= EPOLLET;
235
236 if(epoll_ctl(ep_info->ep, op, F->fd, &ep_event) != 0)
237 {
238 rb_lib_log("rb_select_epoll(): epoll_ctl failed: %s",
239 strerror(errno));
240 }
241 }
242
243 }
244 return RB_OK;
245 }
246
247 #ifdef EPOLL_SCHED_EVENT
248 int
249 rb_epoll_supports_event(void)
250 {
251 /* try to detect at runtime if everything we need actually works */
252 timer_t timer;
253 struct sigevent ev;
254 int fd;
255 sigset_t set;
256
257 if(can_do_event == 1)
258 return 1;
259 if(can_do_event == -1)
260 return 0;
261
262 ev.sigev_signo = SIGVTALRM;
263 ev.sigev_notify = SIGEV_SIGNAL;
264 if(timer_create(CLOCK_REALTIME, &ev, &timer) != 0)
265 {
266 can_do_event = -1;
267 return 0;
268 }
269 timer_delete(timer);
270 sigemptyset(&set);
271 fd = signalfd(-1, &set, 0);
272 if(fd < 0)
273 {
274 can_do_event = -1;
275 return 0;
276 }
277 close(fd);
278 can_do_event = 1;
279 return 1;
280 }
281
282
283 /* bleh..work around a glibc header bug on 32bit systems */
284 struct our_signalfd_siginfo {
285 uint32_t signo;
286 int32_t err;
287 int32_t code;
288 uint32_t pid;
289 uint32_t uid;
290 int32_t fd;
291 uint32_t tid;
292 uint32_t band;
293 uint32_t overrun;
294 uint32_t trapno;
295 int32_t status;
296 int32_t svint;
297 uint64_t svptr;
298 uint64_t utime;
299 uint64_t stime;
300 uint64_t addr;
301 uint8_t pad[48];
302 };
303
304
305 #define SIGFDIOV_COUNT 16
306 static void
307 signalfd_handler(rb_fde_t *F, void *data)
308 {
309 static struct our_signalfd_siginfo fdsig[SIGFDIOV_COUNT];
310 static struct iovec iov[SIGFDIOV_COUNT];
311 struct ev_entry *ev;
312 int ret, x;
313
314 for(x = 0; x < SIGFDIOV_COUNT; x++)
315 {
316 iov[x].iov_base = &fdsig[x];
317 iov[x].iov_len = sizeof(struct our_signalfd_siginfo);
318 }
319
320 while(1)
321 {
322 ret = readv(rb_get_fd(F), iov, SIGFDIOV_COUNT);
323 if(ret == 0 || (ret < 0 && !rb_ignore_errno(errno)))
324 {
325 rb_close(F);
326 rb_epoll_init_event();
327 return;
328 }
329
330 if(ret < 0)
331 {
332 rb_setselect(F, RB_SELECT_READ, signalfd_handler, NULL);
333 return;
334 }
335 for(x = 0; x < ret / (int)sizeof(struct signalfd_siginfo); x++)
336 {
337 ev = (struct ev_entry *)fdsig[x].svptr;
338 if(ev == NULL)
339 continue;
340 rb_run_event(ev);
341 }
342 }
343 }
344
345 void
346 rb_epoll_init_event(void)
347 {
348 sigset_t ss;
349 rb_fde_t *F;
350 int sfd;
351 sigemptyset(&ss);
352 sigaddset(&ss, RTSIGNAL);
353 sigprocmask(SIG_BLOCK, &ss, 0);
354 sigemptyset(&ss);
355 sigaddset(&ss, RTSIGNAL);
356 sfd = signalfd(-1, &ss, 0);
357 if(sfd == -1) {
358 can_do_event = -1;
359 return;
360 }
361 F = rb_open(sfd, RB_FD_UNKNOWN, "signalfd");
362 rb_set_nb(F);
363 signalfd_handler(F, NULL);
364 }
365
366 int
367 rb_epoll_sched_event(struct ev_entry *event, int when)
368 {
369 timer_t *id;
370 struct sigevent ev;
371 struct itimerspec ts;
372
373 memset(&ev, 0, sizeof(&ev));
374 event->comm_ptr = rb_malloc(sizeof(timer_t));
375 id = event->comm_ptr;
376 ev.sigev_notify = SIGEV_SIGNAL;
377 ev.sigev_signo = RTSIGNAL;
378 ev.sigev_value.sival_ptr = event;
379
380 if (timer_create(CLOCK_REALTIME, &ev, id) < 0)
381 {
382 rb_lib_log("timer_create: %s\n", strerror(errno));
383 return 0;
384 }
385 memset(&ts, 0, sizeof(ts));
386 ts.it_value.tv_sec = when;
387 ts.it_value.tv_nsec = 0;
388 if(event->frequency != 0)
389 ts.it_interval = ts.it_value;
390
391 if(timer_settime(*id, 0, &ts, NULL) < 0)
392 {
393 rb_lib_log("timer_settime: %s\n", strerror(errno));
394 return 0;
395 }
396 return 1;
397 }
398
399 void
400 rb_epoll_unsched_event(struct ev_entry *event)
401 {
402 timer_delete(*((timer_t *)event->comm_ptr));
403 rb_free(event->comm_ptr);
404 event->comm_ptr = NULL;
405 }
406 #endif /* EPOLL_SCHED_EVENT */
407
408 #else /* epoll not supported here */
409 int
410 rb_init_netio_epoll(void)
411 {
412 return ENOSYS;
413 }
414
415 void
416 rb_setselect_epoll(rb_fde_t * F, unsigned int type, PF * handler, void *client_data)
417 {
418 errno = ENOSYS;
419 return;
420 }
421
422 int
423 rb_select_epoll(long delay)
424 {
425 errno = ENOSYS;
426 return -1;
427 }
428
429 int
430 rb_setup_fd_epoll(rb_fde_t * F)
431 {
432 errno = ENOSYS;
433 return -1;
434 }
435
436
437 #endif
438
439 #if !defined(USING_EPOLL) || !defined(EPOLL_SCHED_EVENT)
440 void rb_epoll_init_event(void)
441 {
442 return;
443 }
444
445 int
446 rb_epoll_sched_event(struct ev_entry *event, int when)
447 {
448 errno = ENOSYS;
449 return -1;
450 }
451
452 void
453 rb_epoll_unsched_event(struct ev_entry *event)
454 {
455 return;
456 }
457
458 int
459 rb_epoll_supports_event(void)
460 {
461 errno = ENOSYS;
462 return 0;
463 }
464 #endif /* !USING_EPOLL || !EPOLL_SCHED_EVENT */