move shm code out of trinity.c into own file
[trinity.git] / sockets.c
blob27e08ceddd33c1f9aacbff2f62adaf609009acc2
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;
30 __unused__ int ret;
31 struct sockaddr *sa = NULL;
32 socklen_t salen;
33 struct sockopt so = { 0, 0, 0, 0 };
35 fd = socket(domain, type, protocol);
36 if (fd == -1)
37 return fd;
39 shm->sockets[nr_sockets].fd = fd;
40 shm->sockets[nr_sockets].triplet.family = domain;
41 shm->sockets[nr_sockets].triplet.type = type;
42 shm->sockets[nr_sockets].triplet.protocol = protocol;
44 output(2, "fd[%i] = domain:%i (%s) type:0x%x protocol:%i\n",
45 fd, domain, get_proto_name(domain), type, protocol);
47 nr_sockets++;
49 /* Set some random socket options. */
50 sso_socket(&shm->sockets[nr_sockets].triplet, &so, fd);
52 /* Sometimes, listen on created sockets. */
53 if (rand_bool()) {
54 /* fake a sockaddr. */
55 generate_sockaddr((unsigned long **) &sa, (unsigned long *) &salen, domain);
57 ret = bind(fd, sa, salen);
58 /* if (ret == -1)
59 debugf("bind: %s\n", strerror(errno));
60 else
61 debugf("bind: success!\n");
63 ret = listen(fd, (rand() % 2) + 1);
64 /* if (ret == -1)
65 debugf("listen: %s\n", strerror(errno));
66 else
67 debugf("listen: success!\n");
70 if (sa != NULL)
71 free(sa);
73 return fd;
76 static void lock_cachefile(int cachefile, int type)
78 struct flock fl = {
79 .l_len = 0,
80 .l_start = 0,
81 .l_whence = SEEK_SET,
84 fl.l_pid = getpid();
85 fl.l_type = type;
87 if (verbose)
88 output(2, "waiting on lock for cachefile\n");
90 if (fcntl(cachefile, F_SETLKW, &fl) == -1) {
91 perror("fcntl F_SETLKW");
92 exit(1);
95 if (verbose)
96 output(2, "took lock for cachefile\n");
99 static void unlock_cachefile(int cachefile)
101 struct flock fl = {
102 .l_len = 0,
103 .l_start = 0,
104 .l_whence = SEEK_SET,
107 fl.l_pid = getpid();
108 fl.l_type = F_UNLCK;
110 if (fcntl(cachefile, F_SETLK, &fl) == -1) {
111 perror("fcntl F_UNLCK F_SETLK ");
112 exit(1);
115 if (verbose)
116 output(2, "dropped lock for cachefile\n");
119 static void generate_sockets(void)
121 int fd, n;
122 int cachefile;
123 unsigned int nr_to_create = NR_SOCKET_FDS;
124 unsigned int buffer[3];
126 cachefile = creat(cachefilename, S_IWUSR|S_IRUSR);
127 if (cachefile < 0) {
128 outputerr("Couldn't open cachefile for writing! (%s)\n",
129 strerror(errno));
130 exit(EXIT_FAILURE);
133 lock_cachefile(cachefile, F_WRLCK);
136 * Don't loop forever if all protos all are disabled.
138 if (!do_specific_proto) {
139 for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) {
140 if (!no_protos[n])
141 break;
144 if (n >= (int)ARRAY_SIZE(no_protos))
145 nr_to_create = 0;
148 while (nr_to_create > 0) {
150 struct socket_triplet st;
152 if (shm->exit_reason != STILL_RUNNING) {
153 close(cachefile);
154 return;
157 for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) {
159 if (do_specific_proto == TRUE)
160 st.family = specific_proto;
162 if (get_proto_name(st.family) == NULL)
163 goto skip;
165 BUG_ON(st.family >= ARRAY_SIZE(no_protos));
166 if (no_protos[st.family])
167 goto skip;
169 if (sanitise_socket_triplet(&st) == -1)
170 rand_proto_type(&st);
172 fd = open_socket(st.family, st.type, st.protocol);
173 if (fd > -1) {
174 nr_to_create--;
176 buffer[0] = st.family;
177 buffer[1] = st.type;
178 buffer[2] = st.protocol;
179 n = write(cachefile, &buffer, sizeof(int) * 3);
180 if (n == -1) {
181 outputerr("something went wrong writing the cachefile!\n");
182 exit(EXIT_FAILURE);
185 if (nr_to_create == 0)
186 goto done;
187 } else {
188 //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family));
190 skip:
192 /* check for ctrl-c */
193 if (shm->exit_reason != STILL_RUNNING)
194 return;
196 //FIXME: If we've passed -P and we're spinning here without making progress
197 // then we should abort after a few hundred loops.
201 done:
202 unlock_cachefile(cachefile);
204 output(1, "created %d sockets\n", nr_sockets);
206 close(cachefile);
210 void close_sockets(void)
212 unsigned int i;
213 int fd;
214 int r = 0;
215 struct linger ling = { .l_onoff = FALSE, };
217 for (i = 0; i < nr_sockets; i++) {
219 //FIXME: This is a workaround for a weird bug where we hang forevre
220 // waiting for bluetooth sockets when we setsockopt.
221 // Hopefully at some point we can remove this when someone figures out what's going on.
222 if (shm->sockets[i].triplet.family == PF_BLUETOOTH)
223 continue;
225 /* Grab an fd, and nuke it before someone else uses it. */
226 fd = shm->sockets[i].fd;
227 shm->sockets[i].fd = 0;
229 /* disable linger */
230 r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));
231 if (r)
232 perror("setsockopt");
234 r = shutdown(fd, SHUT_RDWR);
235 if (r)
236 perror("shutdown");
238 if (close(fd) != 0)
239 output(1, "failed to close socket [%d:%d:%d].(%s)\n",
240 shm->sockets[i].triplet.family,
241 shm->sockets[i].triplet.type,
242 shm->sockets[i].triplet.protocol,
243 strerror(errno));
246 nr_sockets = 0;
249 void open_sockets(void)
251 int cachefile;
252 unsigned int domain, type, protocol;
253 unsigned int buffer[3];
254 int bytesread=-1;
255 int fd;
257 cachefile = open(cachefilename, O_RDONLY);
258 if (cachefile < 0) {
259 output(1, "Couldn't find socket cachefile. Regenerating.\n");
260 generate_sockets();
261 return;
264 lock_cachefile(cachefile, F_RDLCK);
266 while (bytesread != 0) {
267 bytesread = read(cachefile, buffer, sizeof(int) * 3);
268 if (bytesread == 0)
269 break;
271 domain = buffer[0];
272 type = buffer[1];
273 protocol = buffer[2];
275 if ((do_specific_proto == TRUE && domain != specific_proto) ||
276 (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) {
277 output(1, "ignoring socket cachefile due to specific "
278 "protocol request (or protocol disabled), "
279 "and stale data in cachefile.\n");
280 regenerate:
281 unlock_cachefile(cachefile); /* drop the reader lock. */
282 close(cachefile);
283 unlink(cachefilename);
284 generate_sockets();
285 return;
288 fd = open_socket(domain, type, protocol);
289 if (fd < 0) {
290 output(1, "Cachefile is stale. Need to regenerate.\n");
291 close_sockets();
292 goto regenerate;
295 /* check for ctrl-c */
296 if (shm->exit_reason != STILL_RUNNING) {
297 close(cachefile);
298 return;
302 if (nr_sockets < NR_SOCKET_FDS) {
303 output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets);
304 goto regenerate;
307 output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets);
309 unlock_cachefile(cachefile);
310 close(cachefile);