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