]> git.sesse.net Git - cubemap/blob - acceptor.cpp
Fix a spurious warning.
[cubemap] / acceptor.cpp
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <signal.h>
6 #include <errno.h>
7 #include <arpa/inet.h>
8 #include <sys/ioctl.h>
9 #include <sys/poll.h>
10 #include <sys/socket.h>
11
12 #include "acceptor.h"
13 #include "serverpool.h"
14 #include "state.pb.h"
15
16 using namespace std;
17
18 extern ServerPool *servers;
19 extern volatile bool hupped;
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                 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                 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                 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                 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                 perror("bind");
61                 exit(1);
62         }
63
64         if (socket_type == TCP_SOCKET) {
65                 if (listen(server_sock, 128) == -1) {
66                         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         int ret;
97         do {
98                 ret = close(server_sock);
99         } while (ret == -1 && errno == EINTR);
100
101         if (ret == -1) {
102                 perror("close");
103         }
104 }
105
106 void Acceptor::do_work()
107 {
108         while (!hupped) {
109                 // Since we are non-blocking, we need to wait for the right state first.
110                 // Wait up to 50 ms, then check hupped.
111                 pollfd pfd;
112                 pfd.fd = server_sock;
113                 pfd.events = POLLIN;
114
115                 int nfds = poll(&pfd, 1, 50);
116                 if (nfds == 0 || (nfds == -1 && errno == EINTR)) {
117                         continue;
118                 }
119                 if (nfds == -1) {
120                         perror("poll");
121                         usleep(100000);
122                         continue;
123                 }
124
125                 sockaddr_in6 addr;
126                 socklen_t addrlen = sizeof(addr);
127
128                 // Get a new socket.
129                 int sock = accept(server_sock, reinterpret_cast<sockaddr *>(&addr), &addrlen);
130                 if (sock == -1 && errno == EINTR) {
131                         continue;
132                 }
133                 if (sock == -1) {
134                         perror("accept");
135                         usleep(100000);
136                         continue;
137                 }
138
139                 // Set the socket as nonblocking.
140                 int one = 1;
141                 if (ioctl(sock, FIONBIO, &one) == -1) {
142                         perror("FIONBIO");
143                         exit(1);
144                 }
145
146                 // Pick a server, round-robin, and hand over the socket to it.
147                 servers->add_client(sock);
148         }
149 }