Moved main server loop into its own function
[umurmur.git] / src / server.c
1 /* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
3
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright notice,
11      this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <stdio.h>
32 #include <sys/time.h>
33 #include <sys/poll.h>
34 #include <netinet/tcp.h>
35 #include <sys/socket.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <stdlib.h>
43
44 #include "client.h"
45 #include "conf.h"
46 #include "log.h"
47 #include "timer.h"
48 #include "version.h"
49
50 #define LISTEN_SOCK 0
51 #define TCP_SOCK 0
52 #define UDP_SOCK 1
53
54 #define TCP6_SOCK 2
55 #define UDP6_SOCK 3
56
57 /* globals */
58 int udpsock;
59 bool_t shutdown_server;
60 extern char *bindaddr;
61 extern char *bindaddr6;
62 extern int bindport;
63 extern int bindport6;
64
65 struct sockaddr_storage** Server_setupAddressesAndPorts()
66 {
67   struct sockaddr_storage** addresses;
68
69   struct sockaddr_storage* v4address = calloc(1, sizeof(struct sockaddr_storage));
70   v4address->ss_family = AF_INET;
71   v4address->ss_len = sizeof(struct sockaddr_storage);
72   struct sockaddr_storage* v6address = calloc(1, sizeof(struct sockaddr_storage));
73   v6address->ss_family = AF_INET;
74   v6address->ss_len = sizeof(struct sockaddr_storage);
75
76   int error = 0;
77
78   error = inet_pton(AF_INET, (!bindaddr) ? ((getStrConf(BINDADDR)) ? getStrConf(BINDADDR) : "0.0.0.0")
79                                          : bindaddr, &(((struct sockaddr_in*)v4address)->sin_addr));
80   if (error == 0) Log_fatal("Invalid IPv4 address supplied!");
81
82   error = inet_pton(AF_INET, (!bindaddr6) ? ((getStrConf(BINDADDR6)) ? getStrConf(BINDADDR6) : "::")
83                                          : bindaddr6, &(((struct sockaddr_in6*)v6address)->sin6_addr));
84   if (error == 0) Log_fatal("Invalid IPv6 address supplied!");
85
86   ((struct sockaddr_in*)v4address)->sin_port = htons((bindport) ? bindport : getIntConf(BINDPORT));
87   ((struct sockaddr_in6*)v6address)->sin6_port = htons((bindport) ? bindport : getIntConf(BINDPORT));
88
89   addresses[0] = v4address;
90   addresses[1] = v6address;
91
92   return addresses;
93 }
94
95 void Server_runLoop(struct pollfd* pollfds)
96   {
97   int timeout = 1000, rc, clientcount;
98         etimer_t janitorTimer;
99
100         Timer_init(&janitorTimer);
101
102         while (!shutdown_server) {
103                 struct sockaddr_in remote;
104                 int i;
105
106                 pollfds[UDP_SOCK].revents = 0;
107                 pollfds[TCP_SOCK].revents = 0;
108                 clientcount = Client_getfds(&pollfds[2]);
109
110                 timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
111                 if (timeout <= 0) {
112                         Client_janitor();
113                         Timer_restart(&janitorTimer);
114                         timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
115                 }
116                 rc = poll(pollfds, clientcount + 2, timeout);
117                 if (rc == 0) { /* Timeout */
118                         /* Do maintenance */
119                         Timer_restart(&janitorTimer);
120                         Client_janitor();
121                         continue;
122                 }
123                 if (rc < 0) {
124                         if (errno == EINTR) /* signal */
125                                 continue;
126                         else
127                                 Log_fatal("poll: error %d", errno);
128                 }
129                 if (pollfds[LISTEN_SOCK].revents) { /* New tcp connection */
130                         int tcpfd, flag = 1;
131                         uint32_t addrlen;
132                         addrlen = sizeof(struct sockaddr_in);
133                         tcpfd = accept(pollfds[LISTEN_SOCK].fd, (struct sockaddr*)&remote, &addrlen);
134                         fcntl(tcpfd, F_SETFL, O_NONBLOCK);
135                         setsockopt(tcpfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
136                         Log_debug("Connection from %s port %d\n", inet_ntoa(remote.sin_addr),
137                                           ntohs(remote.sin_port));
138                         if (Client_add(tcpfd, &remote) < 0)
139                                 close(tcpfd);
140                 }
141
142                 if (pollfds[UDP_SOCK].revents) {
143                         Client_read_udp();
144                 }
145                 for (i = 0; i < clientcount; i++) {
146                         if (pollfds[i + 2].revents & POLLIN) {
147                                 Client_read_fd(pollfds[i + 2].fd);
148                         }
149                         if (pollfds[i + 2].revents & POLLOUT) {
150                                 Client_write_fd(pollfds[i + 2].fd);
151                         }
152                 }
153         }
154   }
155
156 void Server_run()
157 {
158         int rc;
159         struct pollfd *pollfds;
160         int tcpsock, sockopt = 1;
161         struct sockaddr_in sin;
162         int val;
163         unsigned short port;
164         in_addr_t inet_address;
165
166         /* max clients + listen sock + udp sock + client connecting that will be disconnected */
167         pollfds = malloc((getIntConf(MAX_CLIENTS) + 3) * sizeof(struct pollfd));
168         if (pollfds == NULL)
169                 Log_fatal("out of memory");
170
171         /* Figure out bind address and port */
172   struct sockaddr_storage** addresses = Server_setupAddressesAndPorts();
173
174         /* Prepare TCP socket */
175         memset(&sin, 0, sizeof(sin));
176         tcpsock = socket(PF_INET, SOCK_STREAM, 0);
177         if (tcpsock < 0)
178                 Log_fatal("socket");
179         if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) != 0)
180                 Log_fatal("setsockopt: %s", strerror(errno));
181         sin.sin_family = AF_INET;
182         sin.sin_port = port;
183         sin.sin_addr.s_addr = inet_address;
184
185         rc = bind(tcpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
186         if (rc < 0) Log_fatal("bind: %s", strerror(errno));
187         rc = listen(tcpsock, 3);
188         if (rc < 0) Log_fatal("listen");
189         fcntl(tcpsock, F_SETFL, O_NONBLOCK);
190
191         pollfds[LISTEN_SOCK].fd = tcpsock;
192         pollfds[LISTEN_SOCK].events = POLLIN;
193
194         /* Prepare UDP socket */
195         memset(&sin, 0, sizeof(sin));
196         udpsock = socket(PF_INET, SOCK_DGRAM, 0);
197         sin.sin_family = AF_INET;
198         sin.sin_port = port;
199         sin.sin_addr.s_addr = inet_address;
200
201         rc = bind(udpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
202         if (rc < 0)
203                 Log_fatal("bind %d %s: %s", getIntConf(BINDPORT), getStrConf(BINDADDR), strerror(errno));
204         val = 0xe0;
205         rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
206         if (rc < 0)
207                 Log_warn("Server: Failed to set TOS for UDP Socket");
208         val = 0x80;
209         rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
210         if (rc < 0)
211                 Log_warn("Server: Failed to set TOS for UDP Socket");
212
213         fcntl(udpsock, F_SETFL, O_NONBLOCK);
214         pollfds[UDP_SOCK].fd = udpsock;
215         pollfds[UDP_SOCK].events = POLLIN | POLLHUP | POLLERR;
216
217         Log_info("uMurmur version %s ('%s') protocol version %d.%d.%d",
218                  UMURMUR_VERSION, UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
219         Log_info("Visit http://code.google.com/p/umurmur/");
220
221         /* Main server loop */
222   Server_runLoop(pollfds);
223
224         /* Disconnect clients */
225         Client_disconnect_all();
226         free(pollfds);
227 }
228
229 void Server_shutdown()
230 {
231         shutdown_server = true;
232 }