]> git.sesse.net Git - cubemap/blob - acceptor.cpp
Merge branch 'master' of /srv/git.sesse.net/www/cubemap
[cubemap] / acceptor.cpp
1 #include <assert.h>
2 #include <errno.h>
3 #include <netinet/in.h>
4 #include <poll.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/ioctl.h>
8 #include <sys/socket.h>
9 #include <unistd.h>
10
11 #include "acceptor.h"
12 #include "log.h"
13 #include "serverpool.h"
14 #include "state.pb.h"
15 #include "util.h"
16
17 using namespace std;
18
19 extern ServerPool *servers;
20
21 int create_server_socket(int port, SocketType socket_type)
22 {
23         int server_sock;
24         if (socket_type == TCP_SOCKET) {
25                 server_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
26         } else {
27                 assert(socket_type == UDP_SOCKET);
28                 server_sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
29         }
30         if (server_sock == -1) {
31                 log_perror("socket");
32                 exit(1);
33         }
34
35         int one = 1;
36         if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
37                 log_perror("setsockopt(SO_REUSEADDR)");
38                 exit(1);
39         }
40
41         // We want dual-stack sockets. (Sorry, OpenBSD and Windows XP...)
42         int zero = 0;
43         if (setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) == -1) {
44                 log_perror("setsockopt(IPV6_V6ONLY)");
45                 exit(1);
46         }
47
48         // Set as non-blocking, so the acceptor thread can notice that we want to shut it down.
49         if (ioctl(server_sock, FIONBIO, &one) == -1) {
50                 log_perror("ioctl(FIONBIO)");
51                 exit(1);
52         }
53
54         sockaddr_in6 addr;
55         memset(&addr, 0, sizeof(addr));
56         addr.sin6_family = AF_INET6;
57         addr.sin6_port = htons(port);
58
59         if (bind(server_sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) == -1) {
60                 log_perror("bind");
61                 exit(1);
62         }
63
64         if (socket_type == TCP_SOCKET) {
65                 if (listen(server_sock, 128) == -1) {
66                         log_perror("listen");
67                         exit(1);
68                 }
69         }
70
71         return server_sock;
72 }
73         
74 Acceptor::Acceptor(int server_sock, int port)
75         : server_sock(server_sock),
76           port(port)
77 {
78 }
79
80 Acceptor::Acceptor(const AcceptorProto &serialized)
81         : server_sock(serialized.server_sock()),
82           port(serialized.port())
83 {
84 }
85
86 AcceptorProto Acceptor::serialize() const
87 {
88         AcceptorProto serialized;
89         serialized.set_server_sock(server_sock);
90         serialized.set_port(port);
91         return serialized;
92 }
93
94 void Acceptor::close_socket()
95 {
96         safe_close(server_sock);
97 }
98
99 void Acceptor::do_work()
100 {
101         while (!should_stop()) {
102                 if (!wait_for_activity(server_sock, POLLIN, NULL)) {
103                         continue;
104                 }
105
106                 sockaddr_in6 addr;
107                 socklen_t addrlen = sizeof(addr);
108
109                 // Get a new socket.
110                 int sock = accept(server_sock, reinterpret_cast<sockaddr *>(&addr), &addrlen);
111                 if (sock == -1 && errno == EINTR) {
112                         continue;
113                 }
114                 if (sock == -1) {
115                         log_perror("accept");
116                         usleep(100000);
117                         continue;
118                 }
119
120                 // Set the socket as nonblocking.
121                 int one = 1;
122                 if (ioctl(sock, FIONBIO, &one) == -1) {
123                         log_perror("FIONBIO");
124                         exit(1);
125                 }
126
127                 // Pick a server, round-robin, and hand over the socket to it.
128                 servers->add_client(sock);
129         }
130 }