1 /*
2 * Copyright 2006 Serge van den Boom <svdb@stack.nl>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #define SOCKET_INTERNAL
20 #define NETDESCRIPTOR_INTERNAL
21 #include "netmanager_bsd.h"
22 #include "ndesc.h"
23 #include "../socket/socket.h"
24
25 #include "ndesc.h"
26 #include "types.h"
27 #include "libs/log.h"
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "netmanager_common.ci"
35 #include "ndindex.ci"
36
37
38 // INV: The following sets only contain sockets present in the netDescriptor
39 // array.
40 static fd_set readSet;
41 static fd_set writeSet;
42 static fd_set exceptionSet;
43
44
45 void
NetManager_init(void)46 NetManager_init(void) {
47 NDIndex_init();
48
49 FD_ZERO(&readSet);
50 FD_ZERO(&writeSet);
51 FD_ZERO(&exceptionSet);
52 }
53
54 void
NetManager_uninit(void)55 NetManager_uninit(void) {
56 NDIndex_uninit();
57 }
58
59 // Register the NetDescriptor with the NetManager.
60 int
NetManager_addDesc(NetDescriptor * nd)61 NetManager_addDesc(NetDescriptor *nd) {
62 int fd;
63 assert(nd->socket != Socket_noSocket);
64 assert(!NDIndex_socketRegistered(nd->socket));
65
66 if (NDIndex_registerNDWithSocket(nd->socket, nd) == -1) {
67 // errno is set
68 return -1;
69 }
70
71 fd = nd->socket->fd;
72 if (nd->readCallback != NULL)
73 FD_SET(fd, &readSet);
74 if (nd->writeCallback != NULL)
75 FD_SET(fd, &writeSet);
76 if (nd->exceptionCallback != NULL)
77 FD_SET(fd, &exceptionSet);
78 return 0;
79 }
80
81 void
NetManager_removeDesc(NetDescriptor * nd)82 NetManager_removeDesc(NetDescriptor *nd) {
83 int fd;
84
85 assert(nd->socket != Socket_noSocket);
86 assert(NDIndex_getNDForSocket(nd->socket) == nd);
87
88 fd = nd->socket->fd;
89 FD_CLR(fd, &readSet);
90 FD_CLR(fd, &writeSet);
91 FD_CLR(fd, &exceptionSet);
92
93 NDIndex_unregisterNDForSocket(nd->socket);
94 }
95
96 void
NetManager_activateReadCallback(NetDescriptor * nd)97 NetManager_activateReadCallback(NetDescriptor *nd) {
98 FD_SET(nd->socket->fd, &readSet);
99 }
100
101 void
NetManager_deactivateReadCallback(NetDescriptor * nd)102 NetManager_deactivateReadCallback(NetDescriptor *nd) {
103 FD_CLR(nd->socket->fd, &readSet);
104 }
105
106 void
NetManager_activateWriteCallback(NetDescriptor * nd)107 NetManager_activateWriteCallback(NetDescriptor *nd) {
108 FD_SET(nd->socket->fd, &writeSet);
109 }
110
111 void
NetManager_deactivateWriteCallback(NetDescriptor * nd)112 NetManager_deactivateWriteCallback(NetDescriptor *nd) {
113 FD_CLR(nd->socket->fd, &writeSet);
114 }
115
116 void
NetManager_activateExceptionCallback(NetDescriptor * nd)117 NetManager_activateExceptionCallback(NetDescriptor *nd) {
118 FD_SET(nd->socket->fd, &exceptionSet);
119 }
120
121 void
NetManager_deactivateExceptionCallback(NetDescriptor * nd)122 NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
123 FD_CLR(nd->socket->fd, &exceptionSet);
124 }
125
126 // This function may be called again from inside a callback function
127 // triggered by this function. BUG: This may result in callbacks being
128 // called multiple times.
129 // This function should however not be called from multiple threads at once.
130 int
NetManager_process(uint32 * timeoutMs)131 NetManager_process(uint32 *timeoutMs) {
132 struct timeval timeout;
133 size_t i;
134 int selectResult;
135 fd_set newReadSet;
136 fd_set newWriteSet;
137 fd_set newExceptionSet;
138 bool bitSet;
139
140 timeout.tv_sec = *timeoutMs / 1000;
141 timeout.tv_usec = (*timeoutMs % 1000) * 1000;
142
143 // Structure assignment:
144 newReadSet = readSet;
145 newWriteSet = writeSet;
146 newExceptionSet = exceptionSet;
147
148 do {
149 selectResult = select(NDIndex_getSelectNumND(),
150 &newReadSet, &newWriteSet, &newExceptionSet, &timeout);
151 // BUG: If select() is restarted because of EINTR, the timeout
152 // may start over. (Linux changes 'timeout' to the time left,
153 // but most other platforms don't.)
154 } while (selectResult == -1 && errno == EINTR);
155 if (selectResult == -1) {
156 int savedErrno = errno;
157 log_add(log_Error, "select() failed: %s.", strerror(errno));
158 errno = savedErrno;
159 *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
160 // XXX: rounding microseconds down. Is that the correct
161 // thing to do?
162 return -1;
163 }
164
165 for (i = 0; i < maxND; i++) {
166 NetDescriptor *nd;
167
168 if (selectResult == 0) {
169 // No more bits set in the fd_sets
170 break;
171 }
172
173 nd = NDIndex_getNDForSocketFd(i);
174 if (nd == NULL)
175 continue;
176
177 bitSet = false;
178 // Is one of the bits in the fd_sets set?
179
180 // A callback may cause a NetDescriptor to be closed. The deletion
181 // of the structure will be scheduled, but will still be
182 // available at least until this function returns.
183
184 if (FD_ISSET(i, &newExceptionSet))
185 {
186 bool closed;
187 bitSet = true;
188 closed = NetManager_doExceptionCallback(nd);
189 if (closed)
190 goto next;
191 }
192
193 if (FD_ISSET(i, &newWriteSet))
194 {
195 bool closed;
196 bitSet = true;
197 closed = NetManager_doWriteCallback(nd);
198 if (closed)
199 goto next;
200 }
201
202 if (FD_ISSET(i, &newReadSet))
203 {
204 bool closed;
205 bitSet = true;
206 closed = NetManager_doReadCallback(nd);
207 if (closed)
208 goto next;
209 }
210
211 next:
212 if (bitSet)
213 selectResult--;
214 }
215
216 *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
217 // XXX: rounding microseconds down. Is that the correct
218 // thing to do?
219 return 0;
220 }
221
222
223
224