Some random changes.
[oweals/tinc.git] / 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         if(fd->fd >= max_fd)
83                 max_fd = ((struct fd *)fds->tail)->fd;
84
85         return avl_del(fds, fd);
86 };
87
88 bool fd_mod(struct fd *fd) {
89         if(fd->read)
90                 FD_SET(fd->fd, &readset);
91         else
92                 FD_CLR(fd->fd, &readset);
93
94         if(fd->write)
95                 FD_SET(fd->fd, &writeset);
96         else
97                 FD_CLR(fd->fd, &writeset);
98
99         if(fd->error)
100                 FD_SET(fd->fd, &errorset);
101         else
102                 FD_CLR(fd->fd, &errorset);
103 }       
104
105 bool fd_run(void) {
106         struct timeval tv;
107         int result;
108         fd_set readtmp, writetmp, errortmp;
109
110         fd_running = true;
111
112         logger(LOG_INFO, "fd: running");
113                 
114         while(fd_running) {
115                 readtmp = readset;
116                 writetmp = writeset;
117                 errortmp = errorset;
118
119                 tv = event_timeout();
120
121                 result = select(max_fd + 1, &readtmp, &writetmp, &errortmp, tv.tv_sec >= 0 ? &tv : NULL);
122
123                 if(result < 0) {
124                         if(errno != EINTR && errno != EAGAIN) {
125                                 logger(LOG_ERR, _("fd: error while waiting for input: %s"), strerror(errno));
126                                 return false;
127                         }
128
129                         continue;
130                 }
131
132                 if(result) {
133                         struct fd *fd;
134                         
135                         avl_foreach(fds, fd, {
136                                 if(fd->read && FD_ISSET(fd->fd, &readtmp))
137                                         fd->read(fd);
138                                 if(fd->write && FD_ISSET(fd->fd, &writetmp))
139                                         fd->write(fd);
140                                 if(fd->error && FD_ISSET(fd->fd, &errortmp))
141                                         fd->error(fd);
142                         });
143                 } else {
144                         event_handle();
145                 }
146         }
147
148         logger(LOG_INFO, "fd: stopping");
149
150         return true;
151 }
152
153 void fd_stop(void) {
154         fd_running = false;
155 }