Handle message/* explicitly in sorting
[xapian.git] / xapian-core / net / tcpserver.cc
blob9f444b0fabeb8dbdf7b3ce32535079f72b9bb309
1 /* tcpserver.cc: class for TCP/IP-based server.
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2015 Olly Betts
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <config.h>
25 #include "tcpserver.h"
27 #include <xapian/error.h>
29 #include "safeerrno.h"
30 #include "safefcntl.h"
31 #include "safenetdb.h"
32 #include "safesyssocket.h"
34 #include "noreturn.h"
35 #include "remoteconnection.h"
36 #include "str.h"
38 #ifdef __WIN32__
39 # include <process.h> /* _beginthreadex, _endthreadex */
40 #else
41 # include <netinet/in_systm.h>
42 # include <netinet/in.h>
43 # include <netinet/ip.h>
44 # include <netinet/tcp.h>
45 # include <arpa/inet.h>
46 # include <signal.h>
47 # include <sys/wait.h>
48 #endif
50 #include <iostream>
52 #include <cstring>
53 #include <cstdio> // For sprintf() on __WIN32__ or cygwin.
54 #include <cstdlib>
55 #include <sys/types.h>
57 using namespace std;
59 // Handle older systems.
60 #if !defined SIGCHLD && defined SIGCLD
61 # define SIGCHLD SIGCLD
62 #endif
64 #ifdef __WIN32__
65 // We must call closesocket() (instead of just close()) under __WIN32__ or
66 // else the socket remains in the CLOSE_WAIT state.
67 # define CLOSESOCKET(S) closesocket(S)
68 #else
69 # define CLOSESOCKET(S) close(S)
70 #endif
72 /// The TcpServer constructor, taking a database and a listening port.
73 TcpServer::TcpServer(const std::string & host, int port, bool tcp_nodelay,
74 bool verbose_)
76 #if defined __CYGWIN__ || defined __WIN32__
77 mutex(NULL),
78 #endif
79 listen_socket(get_listening_socket(host, port, tcp_nodelay
80 #if defined __CYGWIN__ || defined __WIN32__
81 , mutex
82 #endif
83 )),
84 verbose(verbose_)
88 int
89 TcpServer::get_listening_socket(const std::string & host, int port,
90 bool tcp_nodelay
91 #if defined __CYGWIN__ || defined __WIN32__
92 , HANDLE &mutex
93 #endif
96 struct addrinfo hints;
97 memset(&hints, 0, sizeof(struct addrinfo));
98 hints.ai_family = AF_INET;
99 hints.ai_socktype = SOCK_STREAM;
100 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV;
101 hints.ai_protocol = 0;
102 hints.ai_canonname = NULL;
103 hints.ai_addr = NULL;
104 hints.ai_next = NULL;
106 const char * node = host.empty() ? NULL : host.c_str();
107 struct addrinfo *result;
108 int s = getaddrinfo(node, str(port).c_str(), &hints, &result);
109 if (s != 0) {
110 throw Xapian::NetworkError("Couldn't resolve host " + host,
111 eai_to_xapian(s));
114 int socketfd = -1;
115 int bind_errno = 0;
116 for (struct addrinfo * rp = result; rp != NULL; rp = rp->ai_next) {
117 int socktype = rp->ai_socktype | SOCK_CLOEXEC;
118 int fd = socket(rp->ai_family, socktype, rp->ai_protocol);
119 if (fd == -1)
120 continue;
122 #if !defined __WIN32__ && defined F_SETFD && defined FD_CLOEXEC
123 // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as on
124 // Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC SOCK_CLOEXEC'
125 // to allow '#ifdef SOCK_CLOEXEC' to work.
126 if (SOCK_CLOEXEC == 0)
127 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
128 #endif
130 int retval = 0;
132 if (tcp_nodelay) {
133 int optval = 1;
134 // 4th argument might need to be void* or char* - cast it to char*
135 // since C++ allows implicit conversion to void* but not from
136 // void*.
137 retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
138 reinterpret_cast<char *>(&optval),
139 sizeof(optval));
142 int optval = 1;
143 #if defined __CYGWIN__ || defined __WIN32__
144 // Windows has screwy semantics for SO_REUSEADDR - it allows the user
145 // to bind to a port which is already bound and listening! That's
146 // just not suitable as we don't want multiple processes listening on
147 // the same port, so we guard against that by using a named win32 mutex
148 // object (and we create it in the 'Global namespace' so that this
149 // still works in a Terminal Services environment).
150 char name[64];
151 sprintf(name, "Global\\xapian-tcpserver-listening-%d", port);
152 if ((mutex = CreateMutex(NULL, TRUE, name)) == NULL) {
153 // We failed to create the mutex, probably the error is
154 // ERROR_ACCESS_DENIED, which simply means that TcpServer is
155 // already running on this port but as a different user.
156 } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
157 // The mutex already existed, so TcpServer is already running
158 // on this port.
159 CloseHandle(mutex);
160 mutex = NULL;
162 if (mutex == NULL) {
163 cerr << "Server is already running on port " << port << endl;
164 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
165 // server failed to bind to the requested port.
166 exit(69); // FIXME: calling exit() here isn't ideal...
168 #endif
169 if (retval >= 0) {
170 retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
171 reinterpret_cast<char *>(&optval),
172 sizeof(optval));
175 #if defined SO_EXCLUSIVEADDRUSE
176 // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
177 // security issues from SO_REUSEADDR (which affect *any* listening
178 // process, even if doesn't use SO_REUSEADDR itself). There's still no
179 // way of addressing the issue of not being able to listen on a port
180 // which has closed connections in TIME_WAIT state though.
182 // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
183 // Because of this and the lack support on older versions, we don't
184 // currently check the return value.
185 if (retval >= 0) {
186 (void)setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
187 reinterpret_cast<char *>(&optval),
188 sizeof(optval));
190 #endif
192 if (retval < 0) {
193 int saved_errno = socket_errno(); // note down in case close hits an error
194 CLOSESOCKET(fd);
195 freeaddrinfo(result);
196 throw Xapian::NetworkError("setsockopt failed", saved_errno);
199 if (::bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
200 socketfd = fd;
201 break;
204 // Note down the error code for the first address we try, which seems
205 // likely to be more helpful than the last in the case where they
206 // differ.
207 if (bind_errno == 0)
208 bind_errno = socket_errno();
210 CLOSESOCKET(fd);
213 freeaddrinfo(result);
215 if (socketfd == -1) {
216 if (bind_errno == EADDRINUSE) {
217 cerr << host << ':' << port << " already in use" << endl;
218 // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
219 // server failed to bind to the requested port.
220 exit(69); // FIXME: calling exit() here isn't ideal...
222 if (bind_errno == EACCES) {
223 cerr << "Can't bind to privileged port " << port << endl;
224 // 77 is EX_NOPERM. Scripts can use this to detect if
225 // xapian-tcpsrv failed to bind to the requested port.
226 exit(77); // FIXME: calling exit() here isn't ideal...
228 CLOSESOCKET(socketfd);
229 throw Xapian::NetworkError("bind failed", bind_errno);
232 if (listen(socketfd, 5) < 0) {
233 int saved_errno = socket_errno(); // note down in case close hits an error
234 CLOSESOCKET(socketfd);
235 throw Xapian::NetworkError("listen failed", saved_errno);
237 return socketfd;
241 TcpServer::accept_connection()
243 struct sockaddr_in remote_address;
244 SOCKLEN_T remote_address_size = sizeof(remote_address);
245 // accept connections
246 int con_socket = accept(listen_socket,
247 reinterpret_cast<sockaddr *>(&remote_address),
248 &remote_address_size);
250 if (con_socket < 0) {
251 #ifdef __WIN32__
252 if (WSAGetLastError() == WSAEINTR) {
253 // Our CtrlHandler function closed the socket.
254 if (mutex) CloseHandle(mutex);
255 mutex = NULL;
256 return -1;
258 #endif
259 throw Xapian::NetworkError("accept failed", socket_errno());
262 if (remote_address_size != sizeof(remote_address)) {
263 throw Xapian::NetworkError("accept: unexpected remote address size");
266 if (verbose) {
267 char buf[INET_ADDRSTRLEN];
268 #ifndef __WIN32__
269 // Under __WIN32__, inet_ntop()'s second parameter isn't const for some
270 // reason. We don't currently use inet_ntop() there, but allow for a
271 // non-const second parameter in case it's more widespread.
272 void * src = &remote_address.sin_addr;
273 const char * r = inet_ntop(AF_INET, src, buf, sizeof(buf));
274 if (!r)
275 throw Xapian::NetworkError("inet_ntop failed", errno);
276 #else
277 // inet_ntop() isn't always available, at least with mingw.
278 // WSAAddressToString() supports both IPv4 and IPv6, so just use that.
279 DWORD size = sizeof(buf);
280 if (WSAAddressToString(reinterpret_cast<sockaddr*>(&remote_address),
281 sizeof(remote_address), NULL, buf, &size) != 0) {
282 throw Xapian::NetworkError("WSAAddressToString failed",
283 WSAGetLastError());
285 const char * r = buf;
286 #endif
287 int port = remote_address.sin_port;
288 cout << "Connection from " << r << ", port " << port << endl;
291 return con_socket;
294 TcpServer::~TcpServer()
296 CLOSESOCKET(listen_socket);
297 #if defined __CYGWIN__ || defined __WIN32__
298 if (mutex) CloseHandle(mutex);
299 #endif
302 #ifdef HAVE_FORK
303 // A fork() based implementation.
304 void
305 TcpServer::run_once()
307 int connected_socket = accept_connection();
308 pid_t pid = fork();
309 if (pid == 0) {
310 // Child process.
311 close(listen_socket);
313 handle_one_connection(connected_socket);
314 close(connected_socket);
316 if (verbose) cout << "Connection closed." << endl;
317 exit(0);
320 // Parent process.
322 if (pid < 0) {
323 // fork() failed
324 int saved_errno = socket_errno(); // note down in case close hits an error
325 close(connected_socket);
326 throw Xapian::NetworkError("fork failed", saved_errno);
329 close(connected_socket);
332 extern "C" {
334 XAPIAN_NORETURN(static void on_SIGTERM(int /*sig*/));
336 static void
337 on_SIGTERM(int /*sig*/)
339 signal(SIGTERM, SIG_DFL);
340 /* terminate all processes in my process group */
341 #ifdef HAVE_KILLPG
342 killpg(0, SIGTERM);
343 #else
344 kill(0, SIGTERM);
345 #endif
346 exit(0);
349 #ifdef HAVE_WAITPID
350 static void
351 on_SIGCHLD(int /*sig*/)
353 int status;
354 while (waitpid(-1, &status, WNOHANG) > 0);
356 #endif
360 void
361 TcpServer::run()
363 // Handle connections until shutdown.
365 // Set up signal handlers.
366 #ifdef HAVE_WAITPID
367 signal(SIGCHLD, on_SIGCHLD);
368 #else
369 signal(SIGCHLD, SIG_IGN);
370 #endif
371 signal(SIGTERM, on_SIGTERM);
373 while (true) {
374 try {
375 run_once();
376 } catch (const Xapian::Error &e) {
377 // FIXME: better error handling.
378 cerr << "Caught " << e.get_description() << endl;
379 } catch (...) {
380 // FIXME: better error handling.
381 cerr << "Caught exception." << endl;
386 #elif defined __WIN32__
388 // A threaded, Windows specific, implementation.
390 /** The socket which will be closed by CtrlHandler.
392 * FIXME - is there any way to avoid using a global variable here?
394 static const int *pShutdownSocket = NULL;
396 /// Console interrupt handler.
397 static BOOL
398 CtrlHandler(DWORD fdwCtrlType)
400 switch (fdwCtrlType) {
401 case CTRL_C_EVENT:
402 case CTRL_CLOSE_EVENT:
403 // Console is about to die.
404 // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
405 // confirmation dialog asking if we really are sure.
406 case CTRL_LOGOFF_EVENT:
407 case CTRL_SHUTDOWN_EVENT:
408 // These 2 will probably need to change when we get service
409 // support - the service will prevent these being seen, so only
410 // apply interactively.
411 cout << "Shutting down..." << endl;
412 break; // default behaviour
413 case CTRL_BREAK_EVENT:
414 // This (probably) means the developer is struggling to get
415 // things to behave, and really wants to shutdown so let the OS
416 // handle Ctrl+Break in the default way.
417 cout << "Ctrl+Break: aborting process" << endl;
418 return FALSE;
419 default:
420 cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
421 return FALSE;
424 // Note: close() does not cause a blocking accept() call to terminate.
425 // However, it appears closesocket() does. This is much easier than trying
426 // to setup a non-blocking accept().
427 if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
428 // We failed to close the socket, so just let the OS handle the
429 // event in the default way.
430 return FALSE;
433 pShutdownSocket = NULL;
434 return TRUE; // Tell the OS that we've handled the event.
437 /// Structure which is used to pass parameters to the new threads.
438 struct thread_param
440 thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
441 TcpServer *server;
442 int connected_socket;
445 /// The thread entry-point.
446 static unsigned __stdcall
447 run_thread(void * param_)
449 thread_param * param(reinterpret_cast<thread_param *>(param_));
450 int socket = param->connected_socket;
452 param->server->handle_one_connection(socket);
453 closesocket(socket);
455 delete param;
457 _endthreadex(0);
458 return 0;
461 void
462 TcpServer::run()
464 // Handle connections until shutdown.
466 // Set up the shutdown handler - this is a bit hacky, and sadly involves
467 // a global variable.
468 pShutdownSocket = &listen_socket;
469 if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
470 throw Xapian::NetworkError("Failed to install shutdown handler");
472 while (true) {
473 try {
474 int connected_socket = accept_connection();
475 if (connected_socket == -1)
476 return; // Shutdown has happened
478 // Spawn a new thread to handle the connection.
479 // (This seems like lots of hoops just to end up calling
480 // this->handle_one_connection() on a new thread. There might be a
481 // better way...)
482 thread_param *param = new thread_param(this, connected_socket);
483 HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
484 if (hthread == 0) {
485 // errno holds the error code from _beginthreadex, and
486 // closesocket() doesn't set errno.
487 closesocket(connected_socket);
488 throw Xapian::NetworkError("_beginthreadex failed", errno);
491 // FIXME: keep track of open thread handles so we can gracefully
492 // close each thread down. OTOH, when we want to kill them all its
493 // likely to mean the process is on its way down, so it doesn't
494 // really matter...
495 CloseHandle(hthread);
496 } catch (const Xapian::Error &e) {
497 // FIXME: better error handling.
498 cerr << "Caught " << e.get_description() << endl;
499 } catch (...) {
500 // FIXME: better error handling.
501 cerr << "Caught exception." << endl;
506 void
507 TcpServer::run_once()
509 // Run a single request on the current thread.
510 int fd = accept_connection();
511 handle_one_connection(fd);
512 closesocket(fd);
515 #else
516 # error Neither HAVE_FORK nor __WIN32__ are defined.
517 #endif