More work toward 1.2.0:
[umurmur.git] / src / server.c
1 /* Copyright (C) 2009, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2009, 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
43 #include "client.h"
44 #include "conf.h"
45 #include "log.h"
46 #include "timer.h"
47
48 #define LISTEN_SOCK 0
49 #define TCP_SOCK 0
50 #define UDP_SOCK 1
51
52 int udpsock; /* XXX restructure! */
53 bool_t shutdown_server;
54
55 void Server_run()
56 {
57         int timeout = 1000, rc;
58         struct pollfd *pollfds;
59         int tcpsock, sockopt = 1;
60         struct sockaddr_in sin;
61         int val, clientcount;
62         etimer_t janitorTimer;
63
64         /* max clients + listen sock + udp sock + client connecting that will be disconnected */
65         pollfds = malloc((getIntConf(MAX_CLIENTS) + 3) * sizeof(struct pollfd));
66         if (pollfds == NULL)
67                 Log_fatal("out of memory");
68         
69         /* Prepare TCP socket */
70         memset(&sin, 0, sizeof(sin));
71         tcpsock = socket(PF_INET, SOCK_STREAM, 0);
72         if (tcpsock < 0)
73                 Log_fatal("socket");
74         if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) != 0)
75                 Log_fatal("setsockopt: %s", strerror(errno));
76         sin.sin_family = AF_INET;
77         sin.sin_port = htons(getIntConf(BINDPORT));
78         sin.sin_addr.s_addr = inet_addr(getStrConf(BINDADDR)) ==  -1 ? inet_addr("0.0.0.0") : inet_addr(getStrConf(BINDADDR));
79         rc = bind(tcpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
80         if (rc < 0) Log_fatal("bind: %s", strerror(errno));
81         rc = listen(tcpsock, 3);
82         if (rc < 0) Log_fatal("listen");
83         fcntl(tcpsock, F_SETFL, O_NONBLOCK);
84         
85         pollfds[LISTEN_SOCK].fd = tcpsock;
86         pollfds[LISTEN_SOCK].events = POLLIN;
87
88         /* Prepare UDP socket */
89         memset(&sin, 0, sizeof(sin));
90         udpsock = socket(PF_INET, SOCK_DGRAM, 0);
91         sin.sin_family = AF_INET;
92         sin.sin_port = htons(getIntConf(BINDPORT));
93         sin.sin_addr.s_addr = inet_addr(getStrConf(BINDADDR)) ==  -1 ? inet_addr("0.0.0.0") : inet_addr(getStrConf(BINDADDR));
94         rc = bind(udpsock, (struct sockaddr *) &sin, sizeof (struct sockaddr_in));
95         if (rc < 0)
96                 Log_fatal("bind %d %s: %s", getIntConf(BINDPORT), getStrConf(BINDADDR), strerror(errno));
97         val = 0xe0;
98         rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
99         if (rc < 0)
100                 Log_fatal("Server: Failed to set TOS for UDP Socket");
101         val = 0x80;
102         rc = setsockopt(udpsock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
103         if (rc < 0)
104                 Log_fatal("Server: Failed to set TOS for UDP Socket");
105         
106         fcntl(udpsock, F_SETFL, O_NONBLOCK);
107         pollfds[UDP_SOCK].fd = udpsock;
108         pollfds[UDP_SOCK].events = POLLIN | POLLHUP | POLLERR;
109         
110         Timer_init(&janitorTimer);
111         
112         Log_info("uMurmur voicechat server started -- http://code.google.com/p/umurmur/");
113
114         /* Main server loop */
115         while (!shutdown_server) {
116                 struct sockaddr_in remote;
117                 int i;
118                 
119                 pollfds[UDP_SOCK].revents = 0;
120                 pollfds[TCP_SOCK].revents = 0;
121                 clientcount = Client_getfds(&pollfds[2]);
122                 
123                 timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
124                 if (timeout <= 0) {
125                         Client_janitor();
126                         Timer_restart(&janitorTimer);
127                         timeout = (int)(1000000LL - (int64_t)Timer_elapsed(&janitorTimer)) / 1000LL;
128                 }
129                 rc = poll(pollfds, clientcount + 2, timeout);
130                 if (rc == 0) { /* Timeout */
131                         /* Do maintenance */
132                         Timer_restart(&janitorTimer);
133                         Client_janitor();
134                         continue;
135                 }
136                 if (rc < 0) {
137                         if (errno == EINTR) /* signal */
138                                 continue;
139                         else
140                                 Log_fatal("poll: error %d", errno);
141                 }
142                 if (pollfds[LISTEN_SOCK].revents) { /* New tcp connection */
143                         int tcpfd, flag = 1;
144                         uint32_t addrlen;
145                         addrlen = sizeof(struct sockaddr_in);
146                         tcpfd = accept(pollfds[LISTEN_SOCK].fd, (struct sockaddr*)&remote, &addrlen);
147                         fcntl(tcpfd, F_SETFL, O_NONBLOCK);
148                         setsockopt(tcpfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
149                         Log_info("Connection from %s port %d\n", inet_ntoa(remote.sin_addr),
150                                          ntohs(remote.sin_port));
151                         Client_add(tcpfd, &remote);
152                 }
153
154                 if (pollfds[UDP_SOCK].revents) {
155                         Client_read_udp();
156                 }
157                 for (i = 0; i < clientcount; i++) {
158                         if (pollfds[i + 2].revents & POLLIN) {
159                                 Client_read_fd(pollfds[i + 2].fd);
160                         }
161                         if (pollfds[i + 2].revents & POLLOUT) {
162                                 Client_write_fd(pollfds[i + 2].fd);
163                         }
164                 }
165         }       
166
167         /* Disconnect clients */
168         Client_disconnect_all();
169         free(pollfds);
170 }
171
172 void Server_shutdown()
173 {
174         shutdown_server = true;
175 }