]> jfr.im git - irc/rqf/shadowircd.git/blob - libratbox/src/epoll.c
strip_colour(): strip ASCII 29 (mIRC 7 italics).
[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 */
27 #define _GNU_SOURCE 1
28
29 #include <libratbox_config.h>
30 #include <ratbox_lib.h>
31 #include <commio-int.h>
32 #include <event-int.h>
33 #if defined(HAVE_EPOLL_CTL) && (HAVE_SYS_EPOLL_H)
34 #define USING_EPOLL
35 #include <fcntl.h>
36 #include <sys/epoll.h>
37
38 #if defined(HAVE_SIGNALFD) && (HAVE_SYS_SIGNALFD_H) && (USE_TIMER_CREATE) && (HAVE_SYS_UIO_H)
39 #include <signal.h>
40 #include <sys/signalfd.h>
41 #include <sys/uio.h>
42 #define EPOLL_SCHED_EVENT 1
43 #endif
44
45 #if defined(USE_TIMERFD_CREATE)
46 #include <sys/timerfd.h>
47 #endif
48
49 #define RTSIGNAL SIGRTMIN
50 struct epoll_info
51 {
52 int ep;
53 struct epoll_event *pfd;
54 int pfd_size;
55 };
56
57 static struct epoll_info *ep_info;
58 static int can_do_event;
59 static int can_do_timerfd;
60
61 /*
62 * rb_init_netio
63 *
64 * This is a needed exported function which will be called to initialise
65 * the network loop code.
66 */
67 int
68 rb_init_netio_epoll(void)
69 {
70 can_do_event = 0; /* shut up gcc */
71 can_do_timerfd = 0;
72 ep_info = rb_malloc(sizeof(struct epoll_info));
73 ep_info->pfd_size = getdtablesize();
74 ep_info->ep = epoll_create(ep_info->pfd_size);
75 if(ep_info->ep < 0)
76 {
77 return -1;
78 }
79 rb_open(ep_info->ep, RB_FD_UNKNOWN, "epoll file descriptor");
80 ep_info->pfd = rb_malloc(sizeof(struct epoll_event) * ep_info->pfd_size);
81
82 return 0;
83 }
84
85 int
86 rb_setup_fd_epoll(rb_fde_t *F)
87 {
88 return 0;
89 }
90
91
92 /*
93 * rb_setselect
94 *
95 * This is a needed exported function which will be called to register
96 * and deregister interest in a pending IO state for a given FD.
97 */
98 void
99 rb_setselect_epoll(rb_fde_t *F, unsigned int type, PF * handler, void *client_data)
100 {
101 struct epoll_event ep_event;
102 int old_flags = F->pflags;
103 int op = -1;
104
105 lrb_assert(IsFDOpen(F));
106
107 /* Update the list, even though we're not using it .. */
108 if(type & RB_SELECT_READ)
109 {
110 if(handler != NULL)
111 F->pflags |= EPOLLIN;
112 else
113 F->pflags &= ~EPOLLIN;
114 F->read_handler = handler;
115 F->read_data = client_data;
116 }
117
118 if(type & RB_SELECT_WRITE)
119 {
120 if(handler != NULL)
121 F->pflags |= EPOLLOUT;
122 else
123 F->pflags &= ~EPOLLOUT;
124 F->write_handler = handler;
125 F->write_data = client_data;
126 }
127
128 if(old_flags == 0 && F->pflags == 0)
129 return;
130 else if(F->pflags <= 0)
131 op = EPOLL_CTL_DEL;
132 else if(old_flags == 0 && F->pflags > 0)
133 op = EPOLL_CTL_ADD;
134 else if(F->pflags != old_flags)
135 op = EPOLL_CTL_MOD;
136
137 if(op == -1)
138 return;
139
140 ep_event.events = F->pflags;
141 ep_event.data.ptr = F;
142
143 if(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD)
144 ep_event.events |= EPOLLET;
145
146 if(epoll_ctl(ep_info->ep, op, F->fd, &ep_event) != 0)
147 {
148 rb_lib_log("rb_setselect_epoll(): epoll_ctl failed: %s", strerror(errno));
149 abort();
150 }
151
152
153 }
154
155 /*
156 * rb_select
157 *
158 * Called to do the new-style IO, courtesy of squid (like most of this
159 * new IO code). This routine handles the stuff we've hidden in
160 * rb_setselect and fd_table[] and calls callbacks for IO ready
161 * events.
162 */
163
164 int
165 rb_select_epoll(long delay)
166 {
167 int num, i, flags, old_flags, op;
168 struct epoll_event ep_event;
169 int o_errno;
170 void *data;
171
172 num = epoll_wait(ep_info->ep, ep_info->pfd, ep_info->pfd_size, delay);
173
174 /* save errno as rb_set_time() will likely clobber it */
175 o_errno = errno;
176 rb_set_time();
177 errno = o_errno;
178
179 if(num < 0 && !rb_ignore_errno(o_errno))
180 return RB_ERROR;
181
182 if(num <= 0)
183 return RB_OK;
184
185 for(i = 0; i < num; i++)
186 {
187 PF *hdl;
188 rb_fde_t *F = ep_info->pfd[i].data.ptr;
189 old_flags = F->pflags;
190 if(ep_info->pfd[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR))
191 {
192 hdl = F->read_handler;
193 data = F->read_data;
194 F->read_handler = NULL;
195 F->read_data = NULL;
196 if(hdl)
197 {
198 hdl(F, data);
199 }
200 }
201
202 if(!IsFDOpen(F))
203 continue;
204 if(ep_info->pfd[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR))
205 {
206 hdl = F->write_handler;
207 data = F->write_data;
208 F->write_handler = NULL;
209 F->write_data = NULL;
210
211 if(hdl)
212 {
213 hdl(F, data);
214 }
215 }
216
217 if(!IsFDOpen(F))
218 continue;
219
220 flags = 0;
221
222 if(F->read_handler != NULL)
223 flags |= EPOLLIN;
224 if(F->write_handler != NULL)
225 flags |= EPOLLOUT;
226
227 if(old_flags != flags)
228 {
229 if(flags == 0)
230 op = EPOLL_CTL_DEL;
231 else
232 op = EPOLL_CTL_MOD;
233 F->pflags = ep_event.events = flags;
234 ep_event.data.ptr = F;
235 if(op == EPOLL_CTL_MOD || op == EPOLL_CTL_ADD)
236 ep_event.events |= EPOLLET;
237
238 if(epoll_ctl(ep_info->ep, op, F->fd, &ep_event) != 0)
239 {
240 rb_lib_log("rb_select_epoll(): epoll_ctl failed: %s",
241 strerror(errno));
242 }
243 }
244
245 }
246 return RB_OK;
247 }
248
249 #ifdef EPOLL_SCHED_EVENT
250 int
251 rb_epoll_supports_event(void)
252 {
253 /* try to detect at runtime if everything we need actually works */
254 timer_t timer;
255 struct sigevent ev;
256 int fd;
257 sigset_t set;
258
259 if(can_do_event == 1)
260 return 1;
261 if(can_do_event == -1)
262 return 0;
263
264 #ifdef USE_TIMERFD_CREATE
265 if((fd = timerfd_create(CLOCK_REALTIME, 0)) >= 0)
266 {
267 close(fd);
268 can_do_event = 1;
269 can_do_timerfd = 1;
270 return 1;
271 }
272 #endif
273
274 ev.sigev_signo = SIGVTALRM;
275 ev.sigev_notify = SIGEV_SIGNAL;
276 if(timer_create(CLOCK_REALTIME, &ev, &timer) != 0)
277 {
278 can_do_event = -1;
279 return 0;
280 }
281 timer_delete(timer);
282 sigemptyset(&set);
283 fd = signalfd(-1, &set, 0);
284 if(fd < 0)
285 {
286 can_do_event = -1;
287 return 0;
288 }
289 close(fd);
290 can_do_event = 1;
291 return 1;
292 }
293
294
295 /* bleh..work around a glibc header bug on 32bit systems */
296 struct our_signalfd_siginfo
297 {
298 uint32_t signo;
299 int32_t err;
300 int32_t code;
301 uint32_t pid;
302 uint32_t uid;
303 int32_t fd;
304 uint32_t tid;
305 uint32_t band;
306 uint32_t overrun;
307 uint32_t trapno;
308 int32_t status;
309 int32_t svint;
310 uint64_t svptr;
311 uint64_t utime;
312 uint64_t stime;
313 uint64_t addr;
314 uint8_t pad[48];
315 };
316
317
318 #define SIGFDIOV_COUNT 16
319 static void
320 signalfd_handler(rb_fde_t *F, void *data)
321 {
322 static struct our_signalfd_siginfo fdsig[SIGFDIOV_COUNT];
323 static struct iovec iov[SIGFDIOV_COUNT];
324 struct ev_entry *ev;
325 int ret, x;
326
327 for(x = 0; x < SIGFDIOV_COUNT; x++)
328 {
329 iov[x].iov_base = &fdsig[x];
330 iov[x].iov_len = sizeof(struct our_signalfd_siginfo);
331 }
332
333 while(1)
334 {
335 ret = readv(rb_get_fd(F), iov, SIGFDIOV_COUNT);
336
337 if(ret == 0 || (ret < 0 && !rb_ignore_errno(errno)))
338 {
339 rb_close(F);
340 rb_epoll_init_event();
341 return;
342 }
343
344 if(ret < 0)
345 {
346 rb_setselect(F, RB_SELECT_READ, signalfd_handler, NULL);
347 return;
348 }
349 for(x = 0; x < ret / (int)sizeof(struct our_signalfd_siginfo); x++)
350 {
351 #if __WORDSIZE == 32 && defined(__sparc__)
352 uint32_t *q = (uint32_t *)&fdsig[x].svptr;
353 ev = (struct ev_entry *)q[0];
354 #else
355 ev = (struct ev_entry *)(uintptr_t)(fdsig[x].svptr);
356
357 #endif
358 if(ev == NULL)
359 continue;
360 rb_run_event(ev);
361 }
362 }
363 }
364
365 void
366 rb_epoll_init_event(void)
367 {
368
369 sigset_t ss;
370 rb_fde_t *F;
371 int sfd;
372 rb_epoll_supports_event();
373 if(!can_do_timerfd)
374 {
375 sigemptyset(&ss);
376 sigaddset(&ss, RTSIGNAL);
377 sigprocmask(SIG_BLOCK, &ss, 0);
378 sigemptyset(&ss);
379 sigaddset(&ss, RTSIGNAL);
380 sfd = signalfd(-1, &ss, 0);
381 if(sfd == -1)
382 {
383 can_do_event = -1;
384 return;
385 }
386 F = rb_open(sfd, RB_FD_UNKNOWN, "signalfd");
387 rb_set_nb(F);
388 signalfd_handler(F, NULL);
389 }
390 }
391
392 static int
393 rb_epoll_sched_event_signalfd(struct ev_entry *event, int when)
394 {
395 timer_t *id;
396 struct sigevent ev;
397 struct itimerspec ts;
398
399 memset(&ev, 0, sizeof(&ev));
400 event->comm_ptr = rb_malloc(sizeof(timer_t));
401 id = event->comm_ptr;
402 ev.sigev_notify = SIGEV_SIGNAL;
403 ev.sigev_signo = RTSIGNAL;
404 ev.sigev_value.sival_ptr = event;
405
406 if(timer_create(CLOCK_REALTIME, &ev, id) < 0)
407 {
408 rb_lib_log("timer_create: %s\n", strerror(errno));
409 return 0;
410 }
411 memset(&ts, 0, sizeof(ts));
412 ts.it_value.tv_sec = when;
413 ts.it_value.tv_nsec = 0;
414 if(event->frequency != 0)
415 ts.it_interval = ts.it_value;
416
417 if(timer_settime(*id, 0, &ts, NULL) < 0)
418 {
419 rb_lib_log("timer_settime: %s\n", strerror(errno));
420 return 0;
421 }
422 return 1;
423 }
424
425 #ifdef USE_TIMERFD_CREATE
426 static void
427 rb_read_timerfd(rb_fde_t *F, void *data)
428 {
429 struct ev_entry *event = (struct ev_entry *)data;
430 int retlen;
431 uint64_t count;
432
433 if(event == NULL)
434 {
435 rb_close(F);
436 return;
437 }
438
439 retlen = rb_read(F, &count, sizeof(count));
440
441 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
442 {
443 rb_close(F);
444 rb_lib_log("rb_read_timerfd: timerfd[%s] closed on error: %s", event->name,
445 strerror(errno));
446 return;
447 }
448 rb_setselect(F, RB_SELECT_READ, rb_read_timerfd, event);
449 rb_run_event(event);
450 }
451
452
453 static int
454 rb_epoll_sched_event_timerfd(struct ev_entry *event, int when)
455 {
456 struct itimerspec ts;
457 static char buf[FD_DESC_SZ + 8];
458 int fd;
459 rb_fde_t *F;
460
461 if((fd = timerfd_create(CLOCK_REALTIME, 0)) < 0)
462 {
463 rb_lib_log("timerfd_create: %s\n", strerror(errno));
464 return 0;
465 }
466
467 memset(&ts, 0, sizeof(ts));
468 ts.it_value.tv_sec = when;
469 ts.it_value.tv_nsec = 0;
470 if(event->frequency != 0)
471 ts.it_interval = ts.it_value;
472
473 if(timerfd_settime(fd, 0, &ts, NULL) < 0)
474 {
475 rb_lib_log("timerfd_settime: %s\n", strerror(errno));
476 close(fd);
477 return 0;
478 }
479 rb_snprintf(buf, sizeof(buf), "timerfd: %s", event->name);
480 F = rb_open(fd, RB_FD_UNKNOWN, buf);
481 rb_set_nb(F);
482 event->comm_ptr = F;
483 rb_setselect(F, RB_SELECT_READ, rb_read_timerfd, event);
484 return 1;
485 }
486 #endif
487
488
489
490 int
491 rb_epoll_sched_event(struct ev_entry *event, int when)
492 {
493 #ifdef USE_TIMERFD_CREATE
494 if(can_do_timerfd)
495 {
496 return rb_epoll_sched_event_timerfd(event, when);
497 }
498 #endif
499 return rb_epoll_sched_event_signalfd(event, when);
500 }
501
502 void
503 rb_epoll_unsched_event(struct ev_entry *event)
504 {
505 #ifdef USE_TIMERFD_CREATE
506 if(can_do_timerfd)
507 {
508 rb_close((rb_fde_t *)event->comm_ptr);
509 event->comm_ptr = NULL;
510 return;
511 }
512 #endif
513 timer_delete(*((timer_t *) event->comm_ptr));
514 rb_free(event->comm_ptr);
515 event->comm_ptr = NULL;
516 }
517 #endif /* EPOLL_SCHED_EVENT */
518
519 #else /* epoll not supported here */
520 int
521 rb_init_netio_epoll(void)
522 {
523 return ENOSYS;
524 }
525
526 void
527 rb_setselect_epoll(rb_fde_t *F, unsigned int type, PF * handler, void *client_data)
528 {
529 errno = ENOSYS;
530 return;
531 }
532
533 int
534 rb_select_epoll(long delay)
535 {
536 errno = ENOSYS;
537 return -1;
538 }
539
540 int
541 rb_setup_fd_epoll(rb_fde_t *F)
542 {
543 errno = ENOSYS;
544 return -1;
545 }
546
547
548 #endif
549
550 #if !defined(USING_EPOLL) || !defined(EPOLL_SCHED_EVENT)
551 void
552 rb_epoll_init_event(void)
553 {
554 return;
555 }
556
557 int
558 rb_epoll_sched_event(struct ev_entry *event, int when)
559 {
560 errno = ENOSYS;
561 return -1;
562 }
563
564 void
565 rb_epoll_unsched_event(struct ev_entry *event)
566 {
567 return;
568 }
569
570 int
571 rb_epoll_supports_event(void)
572 {
573 errno = ENOSYS;
574 return 0;
575 }
576 #endif /* !USING_EPOLL || !EPOLL_SCHED_EVENT */