]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/io.c
removed some Debug-Code.
[ngircd-alex.git] / src / ngircd / io.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  * Please read the file COPYING, README and AUTHORS for more information.
7  *
8  * I/O abstraction interface.
9  * Copyright (c) 2005 Florian Westphal (westphal@foo.fh-furtwangen.de)
10  *
11  */
12
13 #include "portab.h"
14
15 static char UNUSED id[] = "$Id: io.c,v 1.2 2005/07/09 20:23:00 fw Exp $";
16
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "array.h"
26 #include "io.h"
27 #include "log.h"
28
29
30 typedef struct {
31  void (*callback)(int, short);
32  int fd;
33  short what;
34 } io_event;
35
36 #define INIT_IOEVENT    { NULL, -1, 0, NULL }
37 #define IO_ERROR        4
38
39 #ifdef HAVE_EPOLL_CREATE
40 #define IO_USE_EPOLL    1
41 #else
42 # ifdef HAVE_KQUEUE
43 #define IO_USE_KQUEUE   1
44 # else
45 #define IO_USE_SELECT   1
46 #endif
47 #endif
48
49
50 #ifdef IO_USE_EPOLL
51 #include <sys/epoll.h>
52
53 static int io_masterfd;
54 static bool io_event_new_epoll(int fd, short what);
55 static bool io_event_change_epoll(int fd, short what);
56 static int io_dispatch_epoll(struct timeval *tv);
57 #endif
58
59 #ifdef IO_USE_KQUEUE
60 #include <sys/types.h>
61 #include <sys/event.h>
62 static array io_evcache;
63 static int io_masterfd;
64
65 static int io_dispatch_kqueue(struct timeval *tv);
66 static bool io_event_add_kqueue(int, short);
67 #endif
68
69 #ifdef IO_USE_SELECT
70 #include "defines.h"    /* for conn.h */
71 #include "conn.h"       /* for CONN_IDX (needed by resolve.h) */
72 #include "resolve.h"    /* for RES_STAT (needed by conf.h) */
73 #include "conf.h"       /* for Conf_MaxConnections */
74
75 static fd_set readers;
76 static fd_set writers;
77 static int select_maxfd;                /* the select() interface sucks badly */
78 static int io_dispatch_select(struct timeval *tv);
79 #endif
80
81 static array io_events;
82
83 static void io_docallback PARAMS((int fd, short what));
84
85 static io_event *
86 io_event_get(int fd)
87 {
88         io_event *i;
89         assert(fd >= 0);
90         i = (io_event *) array_get(&io_events, sizeof(io_event), fd);
91         assert(i);
92
93         return i;
94 }
95
96
97 bool
98 io_library_init(unsigned int eventsize)
99 {
100 #ifdef IO_USE_EPOLL
101         int ecreate_hint = (int)eventsize;
102         if (ecreate_hint <= 0)
103                 ecreate_hint = 128;
104 #endif
105
106 #ifdef IO_USE_SELECT
107 #ifdef FD_SETSIZE
108         if (eventsize >= FD_SETSIZE)
109                 eventsize = FD_SETSIZE - 1;
110 #endif
111 #endif
112         if (eventsize && !array_alloc(&io_events, sizeof(io_event), eventsize))
113                 eventsize = 0;
114
115 #ifdef IO_USE_EPOLL
116         io_masterfd = epoll_create(ecreate_hint);
117         Log(LOG_INFO,
118             "io subsystem: using epoll (hint size %d), initial io_event maxfd: %u, io_masterfd %d",
119             ecreate_hint, eventsize, io_masterfd);
120         return io_masterfd >= 0;
121 #endif
122 #ifdef IO_USE_SELECT
123         Log(LOG_INFO, "io subsystem: using select, initial io_event maxfd: %u",
124             eventsize);
125         FD_ZERO(&readers);
126         FD_ZERO(&writers);
127 #ifdef FD_SETSIZE
128         if (Conf_MaxConnections >= FD_SETSIZE) {
129                 Log(LOG_WARNING,
130                     "Conf_MaxConnections (%d) exceeds limit (%u), changed Conf_MaxConnections to %u",
131                     Conf_MaxConnections, FD_SETSIZE, FD_SETSIZE - 1);
132
133                 Conf_MaxConnections = FD_SETSIZE - 1;
134         }
135 #else
136         Log(LOG_WARNING,
137             "FD_SETSIZE undefined, don't know how many descriptors select() can handle on your platform");
138 #endif
139         return true;
140 #endif
141 #ifdef IO_USE_KQUEUE
142         io_masterfd = kqueue();
143
144         Log(LOG_INFO,
145             "io subsystem: using kqueue, initial io_event maxfd: %u, io_masterfd %d",
146             eventsize, io_masterfd);
147         return io_masterfd >= 0;
148 #endif
149 }
150
151
152 void
153 io_library_shutdown(void)
154 {
155 #ifdef IO_USE_SELECT
156         FD_ZERO(&readers);
157         FD_ZERO(&writers);
158 #else
159         close(io_masterfd);     /* kqueue, epoll */
160         io_masterfd = -1;
161 #endif
162 #ifdef IO_USE_KQUEUE
163         array_free(&io_evcache);
164 #endif
165         return true;
166 }
167
168
169 bool
170 io_event_setcb(int fd, void (*cbfunc) (int, short))
171 {
172         io_event *i = io_event_get(fd);
173         if (!i)
174                 return false;
175
176         i->callback = cbfunc;
177         return true;
178 }
179
180
181 bool
182 io_event_create(int fd, short what, void (*cbfunc) (int, short))
183 {
184         io_event *i;
185
186         assert(fd >= 0);
187
188 #ifdef IO_USE_SELECT
189 #ifdef FD_SETSIZE
190         if (fd >= FD_SETSIZE) {
191                 Log(LOG_ERR,
192                     "fd %d exceeds FD_SETSIZE (%u) (select can't handle more file descriptors)",
193                     fd, FD_SETSIZE);
194                 return false;
195         }
196 #endif                          /* FD_SETSIZE */
197 #endif                          /* IO_USE_SELECT */
198
199         i = (io_event *) array_alloc(&io_events, sizeof(io_event), fd);
200         if (!i) {
201                 Log(LOG_WARNING,
202                     "array_alloc failed: could not allocate space for %d io_event structures",
203                     fd);
204                 return false;
205         }
206
207         i->fd = fd;
208         i->callback = cbfunc;
209 #ifdef IO_USE_EPOLL
210         i->what = what;
211         return io_event_new_epoll(fd, what);
212 #endif
213 #ifdef IO_USE_KQUEUE
214         i->what = what;
215         return io_event_add_kqueue(fd, what);
216 #endif
217 #ifdef IO_USE_SELECT
218         i->what = 0;
219         return io_event_add(fd, what);
220 #endif
221 }
222
223
224 #ifdef IO_USE_EPOLL
225 static bool
226 io_event_new_epoll(int fd, short what)
227 {
228         struct epoll_event ev = { 0, {0} };
229         ev.data.fd = fd;
230
231         if (what & IO_WANTREAD)
232                 ev.events = EPOLLIN | EPOLLPRI;
233         if (what & IO_WANTWRITE)
234                 ev.events |= EPOLLOUT;
235
236         return epoll_ctl(io_masterfd, EPOLL_CTL_ADD, fd, &ev) == 0;
237 }
238
239
240 static bool
241 io_event_change_epoll(int fd, short what)
242 {
243         struct epoll_event ev = { 0, {0} };
244         ev.data.fd = fd;
245
246         if (what & IO_WANTREAD)
247                 ev.events = EPOLLIN | EPOLLPRI;
248         if (what & IO_WANTWRITE)
249                 ev.events |= EPOLLOUT;
250
251         return epoll_ctl(io_masterfd, EPOLL_CTL_MOD, fd, &ev) == 0;
252 }
253 #endif
254
255 #ifdef IO_USE_KQUEUE
256 static bool
257 io_event_kqueue_commit_cache(void)
258 {
259         struct kevent *events;
260         bool ret;
261         int len = (int) array_length(&io_evcache, sizeof (struct kevent));
262  
263         if (!len) /* nothing to do */
264                 return true;
265
266         assert(len>0);
267
268         if (len < 0) {
269                 array_free(&io_evcache);
270                 return false;
271         }
272
273         events = array_start(&io_evcache);
274
275         assert(events);
276
277         ret = kevent(io_masterfd, events, len, NULL, 0, NULL) == 0;
278         if (ret)
279                 array_trunc(&io_evcache);
280         return ret;
281 }
282
283
284 static bool
285 io_event_add_kqueue(int fd, short what)
286 {
287         struct kevent kev;
288         short filter = 0;
289         unsigned int len = array_length(&io_evcache, sizeof kev);
290         bool ret;
291
292         if (what & IO_WANTREAD)
293                 filter = EVFILT_READ;
294
295         if (what & IO_WANTWRITE)
296                 filter |= EVFILT_WRITE;
297
298         if (len >= 100) {
299                 ret = io_event_kqueue_commit_cache();
300                 if (ret)
301                         array_trunc(&io_evcache);
302         }
303
304         EV_SET(&kev, fd, filter, EV_ADD | EV_ENABLE, 0, 0, NULL);
305         return array_catb(&io_evcache, (char*) &kev, sizeof (kev));
306 }
307 #endif
308
309
310 bool
311 io_event_add(int fd, short what)
312 {
313         io_event *i = io_event_get(fd);
314
315         assert(i);
316
317         if (!i)
318                 return false;
319         if (i->what == what)
320                 return true;
321 #ifdef DEBUG
322         Log(LOG_DEBUG, "io_event_add(): fd %d (arg: %d), what %d.", i->fd, fd, what);
323 #endif
324
325         i->what |= what;
326
327 #ifdef IO_USE_EPOLL
328         return io_event_change_epoll(fd, i->what);
329 #endif
330
331 #ifdef IO_USE_KQUEUE
332         return io_event_add_kqueue(fd, what);
333 #endif
334
335 #ifdef IO_USE_SELECT
336         if (fd > select_maxfd)
337                 select_maxfd = fd;
338
339         if (what & IO_WANTREAD)
340                 FD_SET(fd, &readers);
341         if (what & IO_WANTWRITE)
342                 FD_SET(fd, &writers);
343
344         return true;
345 #endif
346 }
347
348
349 bool
350 io_setnonblock(int fd)
351 {
352         int flags = fcntl(fd, F_GETFL);
353         if (flags == -1)
354                 return false;
355
356 #ifndef O_NONBLOCK
357 #define O_NONBLOCK O_NDELAY
358 #endif
359         flags |= O_NONBLOCK;
360
361         return fcntl(fd, F_SETFL, flags) == 0;
362 }
363
364
365 bool
366 io_close(int fd)
367 {
368         io_event *i = io_event_get(fd);
369         if (i) {
370                 memset(i, 0, sizeof(io_event));
371                 i->fd = -1;
372         }
373 #ifdef IO_USE_SELECT
374         FD_CLR(fd, &writers);
375         FD_CLR(fd, &readers);
376
377         if (fd == select_maxfd)
378                 select_maxfd--;
379 #endif
380 #ifdef IO_USE_KQUEUE
381         if (array_length(&io_evcache, sizeof (struct kevent)))  /* pending data in cache? */
382                 io_event_kqueue_commit_cache();
383 #endif
384         return close(fd) == 0;  /* both epoll an kqueue will remove fd from all sets automatically */
385 }
386
387
388 bool
389 io_event_del(int fd, short what)
390 {
391 #ifdef IO_USE_KQUEUE
392         struct kevent kev;
393         short filter = 0;
394 #endif
395         io_event *i = io_event_get(fd);
396 #ifdef DEBUG
397         Log(LOG_DEBUG, "io_event_del(): trying to delete eventtype %d on fd %d", what, fd);
398 #endif
399         assert(i);
400         if (!i)
401                 return false;
402
403         i->what &= ~what;
404
405 #ifdef IO_USE_EPOLL
406         return io_event_change_epoll(fd, i->what);
407 #endif
408
409 #ifdef IO_USE_KQUEUE
410         if (what & IO_WANTREAD)
411                 filter = EVFILT_READ;
412
413         if (what & IO_WANTWRITE)
414                 filter |= EVFILT_WRITE;
415
416         EV_SET(&kev, fd, filter, EV_DELETE, 0, 0, NULL);
417         return kevent(io_masterfd, &kev, 1, NULL, 0, NULL) == 0;
418 #endif
419
420 #ifdef IO_USE_SELECT
421         if (what & IO_WANTWRITE)
422                 FD_CLR(i->fd, &writers);
423
424         if (what & IO_WANTREAD)
425                 FD_CLR(i->fd, &readers);
426
427         return true;
428 #endif
429 }
430
431
432 #ifdef IO_USE_SELECT
433 static int
434 io_dispatch_select(struct timeval *tv)
435 {
436         fd_set readers_tmp = readers;
437         fd_set writers_tmp = writers;
438         short what;
439         int ret, i;
440         int fds_ready;
441         ret = select(select_maxfd + 1, &readers_tmp, &writers_tmp, NULL, tv);
442         if (ret <= 0)
443                 return ret;
444
445         fds_ready = ret;
446
447         for (i = 0; i <= select_maxfd; i++) {
448                 what = 0;
449                 if (FD_ISSET(i, &readers_tmp)) {
450                         what = IO_WANTREAD;
451                         fds_ready--;
452                 }
453
454                 if (FD_ISSET(i, &writers_tmp)) {
455                         what |= IO_WANTWRITE;
456                         fds_ready--;
457                 }
458                 if (what)
459                         io_docallback(i, what);
460                 if (fds_ready <= 0)
461                         break;
462         }
463
464         return ret;
465 }
466 #endif
467
468
469 #ifdef IO_USE_EPOLL
470 static int
471 io_dispatch_epoll(struct timeval *tv)
472 {
473         time_t sec = tv->tv_sec * 1000;
474         int i, total = 0, ret, timeout = tv->tv_usec + sec;
475         struct epoll_event epoll_ev[100];
476         short type;
477
478         if (timeout < 0)
479                 timeout = 1000;
480
481         do {
482                 ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout);
483                 total += ret;
484                 if (ret <= 0)
485                         return total;
486
487                 for (i = 0; i < ret; i++) {
488                         type = 0;
489                         if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
490                                 type = IO_ERROR;
491
492                         if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
493                                 type |= IO_WANTREAD;
494
495                         if (epoll_ev[i].events & EPOLLOUT)
496                                 type |= IO_WANTWRITE;
497
498                         io_docallback(epoll_ev[i].data.fd, type);
499                 }
500
501                 timeout = 0;
502         } while (ret == 100);
503
504         return total;
505 }
506 #endif
507
508
509 #ifdef IO_USE_KQUEUE
510 static int
511 io_dispatch_kqueue(struct timeval *tv)
512 {
513         int i, total = 0, ret;
514         struct kevent kev[100];
515         struct kevents *newevents;
516         struct timespec ts;
517         int newevents_len;
518         short type;
519         ts.tv_sec = tv->tv_sec;
520         ts.tv_nsec = tv->tv_usec * 1000;
521         
522         do {
523                 newevents_len = array_length(&io_evcache, sizeof (struct kevent));
524                 newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
525                 assert(newevents_len >= 0);
526                 if (newevents_len < 0)
527                         newevents_len = 0;
528 #ifdef DEBUG
529                 if (newevents_len)
530                         assert(newevents);
531 #endif
532
533                 ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts);
534                 if ((newevents_len>0) && ret != -1)
535                         array_trunc(&io_evcache);
536
537                 total += ret;
538                 if (ret <= 0)
539                         return total;
540
541                 for (i = 0; i < ret; i++) {
542                         type = 0;
543                         if (kev[i].flags & EV_EOF)
544                                 type = IO_ERROR;
545
546                         if (kev[i].filter & EV_ERROR)
547                                 type = IO_ERROR;
548
549                         if (kev[i].filter & EVFILT_READ)
550                                 type |= IO_WANTREAD;
551
552                         if (kev[i].filter & EVFILT_WRITE)
553                                 type |= IO_WANTWRITE;
554
555                         io_docallback(kev[i].ident, type);
556                 }
557
558                 ts.tv_sec = 0;
559                 ts.tv_nsec = 0;
560
561         } while (ret == 100);
562
563         return total;
564 }
565 #endif
566
567
568 int
569 io_dispatch(struct timeval *tv)
570 {
571 #ifdef IO_USE_SELECT
572         return io_dispatch_select(tv);
573 #endif
574 #ifdef IO_USE_KQUEUE
575         return io_dispatch_kqueue(tv);
576 #endif
577 #ifdef IO_USE_EPOLL
578         return io_dispatch_epoll(tv);
579 #endif
580 }
581
582
583 /* call the callback function inside the struct matching fd */
584 static void
585 io_docallback(int fd, short what)
586 {
587         io_event *i;
588 #ifdef DEBUG
589         Log(LOG_DEBUG, "doing callback for fd %d, what %d", fd, what);
590 #endif
591         i = io_event_get(fd);
592         assert(i);
593
594         if (i->callback)        /* callback might be 0 if previous callback function called io_close on this fd */
595                 i->callback(fd, (what & IO_ERROR) ? i->what : what);
596         /* if error indicator is set, we return the event(s) the app asked for */
597 }