work around bluetooth linger bug
[trinity.git] / sockets.c
blobd856c30e70b2427124ded8e13c2805251ac6abab
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/stat.h>
11 #include "trinity.h"
12 #include "constants.h"
13 #include "shm.h"
14 #include "net.h"
15 #include "log.h"
16 #include "params.h" // verbose, do_specific_proto
17 #include "random.h"
18 #include "protocols.h"
20 unsigned int nr_sockets = 0;
22 static const char *cachefilename="trinity.socketcache";
24 #define MAX_PER_DOMAIN 5
25 #define MAX_TRIES_PER_DOMAIN 10
27 static int open_socket(unsigned int domain, unsigned int type, unsigned int protocol)
29 int fd, ret;
30 struct sockaddr sa;
31 socklen_t salen;
32 struct sockopt so = { 0, 0, 0, 0 };
34 fd = socket(domain, type, protocol);
35 if (fd == -1)
36 return fd;
38 shm->sockets[nr_sockets].fd = fd;
39 shm->sockets[nr_sockets].triplet.family = domain;
40 shm->sockets[nr_sockets].triplet.type = type;
41 shm->sockets[nr_sockets].triplet.protocol = protocol;
43 output(2, "fd[%i] = domain:%i (%s) type:0x%x protocol:%i\n",
44 fd, domain, get_proto_name(domain), type, protocol);
46 nr_sockets++;
48 /* skip over bluetooth due to weird linger bug */
49 if (domain == PF_BLUETOOTH)
50 goto skip_sso;
52 /* Set some random socket options. */
53 retry_sso:
54 do_setsockopt(&so);
55 ret = setsockopt(fd, so.level, so.optname, (void *)so.optval, so.optlen);
56 if (ret == 0)
57 output(1, "Setsockopt(%lx %lx %lx %lx) on fd %d\n",
58 so.level, so.optname, so.optval, so.optlen, fd);
59 else
60 goto retry_sso;
62 skip_sso:
64 /* Sometimes, listen on created sockets. */
65 if (rand_bool()) {
66 /* fake a sockaddr. */
67 generate_sockaddr((unsigned long *) &sa, (unsigned long *) &salen, domain);
69 ret = bind(fd, &sa, salen);
70 /* if (ret == -1)
71 debugf("bind: %s\n", strerror(errno));
72 else
73 debugf("bind: success!\n");
75 ret = listen(fd, (rand() % 2) + 1);
76 /* if (ret == -1)
77 debugf("listen: %s\n", strerror(errno));
78 else
79 debugf("listen: success!\n");
83 return fd;
86 static void lock_cachefile(int cachefile, int type)
88 struct flock fl = {
89 .l_len = 0,
90 .l_start = 0,
91 .l_whence = SEEK_SET,
94 fl.l_pid = getpid();
95 fl.l_type = type;
97 if (verbose)
98 output(2, "waiting on lock for cachefile\n");
100 if (fcntl(cachefile, F_SETLKW, &fl) == -1) {
101 perror("fcntl F_SETLKW");
102 exit(1);
105 if (verbose)
106 output(2, "took lock for cachefile\n");
109 static void unlock_cachefile(int cachefile)
111 struct flock fl = {
112 .l_len = 0,
113 .l_start = 0,
114 .l_whence = SEEK_SET,
117 fl.l_pid = getpid();
118 fl.l_type = F_UNLCK;
120 if (fcntl(cachefile, F_SETLK, &fl) == -1) {
121 perror("fcntl F_UNLCK F_SETLK ");
122 exit(1);
125 if (verbose)
126 output(2, "dropped lock for cachefile\n");
129 static void generate_sockets(void)
131 int fd, n;
132 int cachefile;
133 unsigned int nr_to_create = NR_SOCKET_FDS;
134 unsigned int buffer[3];
136 cachefile = creat(cachefilename, S_IWUSR|S_IRUSR);
137 if (cachefile < 0) {
138 outputerr("Couldn't open cachefile for writing! (%s)\n",
139 strerror(errno));
140 exit(EXIT_FAILURE);
143 lock_cachefile(cachefile, F_WRLCK);
146 * Don't loop forever if all protos all are disabled.
148 if (!do_specific_proto) {
149 for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) {
150 if (!no_protos[n])
151 break;
154 if (n >= (int)ARRAY_SIZE(no_protos))
155 nr_to_create = 0;
158 while (nr_to_create > 0) {
160 struct socket_triplet st;
162 if (shm->exit_reason != STILL_RUNNING) {
163 close(cachefile);
164 return;
167 for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) {
169 if (do_specific_proto == TRUE)
170 st.family = specific_proto;
172 if (get_proto_name(st.family) == NULL)
173 goto skip;
175 BUG_ON(st.family >= ARRAY_SIZE(no_protos));
176 if (no_protos[st.family])
177 goto skip;
179 if (sanitise_socket_triplet(&st) == -1)
180 rand_proto_type(&st);
182 fd = open_socket(st.family, st.type, st.protocol);
183 if (fd > -1) {
184 nr_to_create--;
186 buffer[0] = st.family;
187 buffer[1] = st.type;
188 buffer[2] = st.protocol;
189 n = write(cachefile, &buffer, sizeof(int) * 3);
190 if (n == -1) {
191 outputerr("something went wrong writing the cachefile!\n");
192 exit(EXIT_FAILURE);
195 if (nr_to_create == 0)
196 goto done;
197 } else {
198 //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family));
200 skip:
202 /* check for ctrl-c */
203 if (shm->exit_reason != STILL_RUNNING)
204 return;
206 //FIXME: If we've passed -P and we're spinning here without making progress
207 // then we should abort after a few hundred loops.
211 done:
212 unlock_cachefile(cachefile);
214 output(1, "created %d sockets\n", nr_sockets);
216 close(cachefile);
220 void close_sockets(void)
222 unsigned int i;
223 int fd;
224 int r = 0;
225 struct linger ling = { .l_onoff = FALSE, };
227 for (i = 0; i < nr_sockets; i++) {
229 //FIXME: This is a workaround for a weird bug where we hang forevre
230 // waiting for bluetooth sockets when we setsockopt.
231 // Hopefully at some point we can remove this when someone figures out what's going on.
232 if (shm->sockets[i].triplet.family == PF_BLUETOOTH)
233 continue;
235 /* Grab an fd, and nuke it before someone else uses it. */
236 fd = shm->sockets[i].fd;
237 shm->sockets[i].fd = 0;
239 /* disable linger */
240 r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));
241 if (r)
242 perror("setsockopt");
244 r = shutdown(fd, SHUT_RDWR);
245 if (r)
246 perror("shutdown");
248 if (close(fd) != 0)
249 output(1, "failed to close socket [%d:%d:%d].(%s)\n",
250 shm->sockets[i].triplet.family,
251 shm->sockets[i].triplet.type,
252 shm->sockets[i].triplet.protocol,
253 strerror(errno));
256 nr_sockets = 0;
259 void open_sockets(void)
261 int cachefile;
262 unsigned int domain, type, protocol;
263 unsigned int buffer[3];
264 int bytesread=-1;
265 int fd;
267 cachefile = open(cachefilename, O_RDONLY);
268 if (cachefile < 0) {
269 output(1, "Couldn't find socket cachefile. Regenerating.\n");
270 generate_sockets();
271 return;
274 lock_cachefile(cachefile, F_RDLCK);
276 while (bytesread != 0) {
277 bytesread = read(cachefile, buffer, sizeof(int) * 3);
278 if (bytesread == 0)
279 break;
281 domain = buffer[0];
282 type = buffer[1];
283 protocol = buffer[2];
285 if ((do_specific_proto == TRUE && domain != specific_proto) ||
286 (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) {
287 output(1, "ignoring socket cachefile due to specific "
288 "protocol request (or protocol disabled), "
289 "and stale data in cachefile.\n");
290 regenerate:
291 unlock_cachefile(cachefile); /* drop the reader lock. */
292 close(cachefile);
293 unlink(cachefilename);
294 generate_sockets();
295 return;
298 fd = open_socket(domain, type, protocol);
299 if (fd < 0) {
300 output(1, "Cachefile is stale. Need to regenerate.\n");
301 close_sockets();
302 goto regenerate;
305 /* check for ctrl-c */
306 if (shm->exit_reason != STILL_RUNNING) {
307 close(cachefile);
308 return;
312 if (nr_sockets < NR_SOCKET_FDS) {
313 output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets);
314 goto regenerate;
317 output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets);
319 unlock_cachefile(cachefile);
320 close(cachefile);