Imported start of brand new codebase for tinc 2.0.
[tinc] / fd / fd.c
1 /*
2     fd.c -- I/O and event multiplexing
3
4     Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id$
21 */
22
23 #include "system.h"
24
25 #include "support/avl.h"
26 #include "support/xalloc.h"
27 #include "fd/event.h"
28 #include "fd/fd.h"
29
30 static fd_set fd_sets[FD_MODES];
31 static int max_fd;
32 static avl_tree_t *fds;
33
34 volatile bool fd_running = false;
35
36 int fd_compare(struct fd *a, struct fd *b) {
37         return (a->fd - b->fd) ?: (a->mode - b->mode);
38 };
39
40 bool fd_init(void) {
41         int i;
42
43         for(i = 0; i < FD_MODES; i++)
44                 FD_ZERO(&fd_sets[i]);
45
46         fds = avl_tree_new((avl_compare_t)fd_compare, NULL);
47
48         event_init();
49 }
50
51 bool fd_exit(void) {
52         event_exit();
53
54         avl_tree_del(fds);
55 }
56
57 bool fd_add(struct fd *fd) {
58         if(!avl_add(fds, fd))
59                 return false;
60
61         FD_SET(fd->fd, &fd_sets[fd->mode]);
62         
63         if(fd->fd > max_fd)
64                 max_fd = fd->fd;
65
66         return true;
67 };
68
69 bool fd_del(struct fd *fd) {
70         FD_CLR(fd->fd, &fd_sets[fd->mode]);
71         
72         if(fd->fd >= max_fd)
73                 max_fd = ((struct fd *)fds->tail)->fd;
74
75         return avl_del(fds, fd);
76 };
77
78 bool fd_run(void) {
79         struct timeval tv;
80         int result;
81         fd_set fd_cur[FD_MODES];
82
83         fd_running = true;
84
85         logger(LOG_INFO, "fd: running");
86                 
87         while(fd_running) {
88                 memcpy(fd_cur, fd_sets, sizeof(fd_cur));
89                 tv = event_timeout();
90
91                 result = select(max_fd + 1, &fd_cur[0], &fd_cur[1], &fd_cur[2], tv.tv_sec >= 0 ? &tv : NULL);
92
93                 if(result < 0) {
94                         if(errno != EINTR && errno != EAGAIN) {
95                                 logger(LOG_ERR, _("fd: error while waiting for input: %s"), strerror(errno));
96                                 return false;
97                         }
98
99                         continue;
100                 }
101
102                 if(result) {
103                         struct fd *fd;
104                         
105                         avl_foreach(fds, fd, {
106                                 if(FD_ISSET(fd->fd, &fd_cur[fd->mode]))
107                                         fd->handler(fd);
108                         });
109                 } else {
110                         event_handle();
111                 }
112         }
113
114         logger(LOG_INFO, "fd: stopping");
115
116         return true;
117 }
118
119 void fd_stop(void) {
120         fd_running = false;
121 }