One directory for source code.
[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 readset, writeset, errorset;
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;
38 };
39
40 bool fd_init(void) {
41         int i;
42
43         FD_ZERO(&readset);
44         FD_ZERO(&writeset);
45         FD_ZERO(&errorset);
46
47         fds = avl_tree_new((avl_compare_t)fd_compare, NULL);
48
49         event_init();
50 }
51
52 bool fd_exit(void) {
53         event_exit();
54
55         avl_tree_del(fds);
56 }
57
58 bool fd_add(struct fd *fd) {
59         if(!avl_add(fds, fd))
60                 return false;
61
62         if(fd->read)
63                 FD_SET(fd->fd, &readset);
64
65         if(fd->write)
66                 FD_SET(fd->fd, &writeset);
67
68         if(fd->error)
69                 FD_SET(fd->fd, &errorset);
70         
71         if(fd->fd > max_fd)
72                 max_fd = fd->fd;
73
74         return true;
75 };
76
77 bool fd_del(struct fd *fd) {
78         FD_CLR(fd->fd, &readset);
79         FD_CLR(fd->fd, &writeset);
80         FD_CLR(fd->fd, &errorset);
81         
82         avl_del(fds, fd);
83
84         if(fds->tail)
85                 max_fd = ((struct fd *)fds->tail->data)->fd;
86         else
87                 max_fd = 0;
88
89         return true;
90 };
91
92 bool fd_mod(struct fd *fd) {
93         if(fd->read)
94                 FD_SET(fd->fd, &readset);
95         else
96                 FD_CLR(fd->fd, &readset);
97
98         if(fd->write)
99                 FD_SET(fd->fd, &writeset);
100         else
101                 FD_CLR(fd->fd, &writeset);
102
103         if(fd->error)
104                 FD_SET(fd->fd, &errorset);
105         else
106                 FD_CLR(fd->fd, &errorset);
107 }       
108
109 bool fd_run(void) {
110         struct timeval tv;
111         int result;
112         fd_set readtmp, writetmp, errortmp;
113
114         fd_running = true;
115
116         logger(LOG_INFO, "fd: running");
117                 
118         while(fd_running) {
119                 readtmp = readset;
120                 writetmp = writeset;
121                 errortmp = errorset;
122
123                 tv = event_timeout();
124
125                 result = select(max_fd + 1, &readtmp, &writetmp, &errortmp, tv.tv_sec >= 0 ? &tv : NULL);
126
127                 if(result < 0) {
128                         if(errno != EINTR && errno != EAGAIN) {
129                                 logger(LOG_ERR, _("fd: error while waiting for input: %s"), strerror(errno));
130                                 return false;
131                         }
132
133                         continue;
134                 }
135
136                 if(result) {
137                         struct fd *fd;
138                         
139                         avl_foreach(fds, fd, {
140                                 if(fd->read && FD_ISSET(fd->fd, &readtmp))
141                                         fd->read(fd);
142                                 if(fd->write && FD_ISSET(fd->fd, &writetmp))
143                                         fd->write(fd);
144                                 if(fd->error && FD_ISSET(fd->fd, &errortmp))
145                                         fd->error(fd);
146                         });
147                 } else {
148                         event_handle();
149                 }
150         }
151
152         logger(LOG_INFO, "fd: stopping");
153
154         return true;
155 }
156
157 void fd_stop(void) {
158         fd_running = false;
159 }