net/tcpserver.cc

Go to the documentation of this file.
00001 /* tcpserver.cc: class for TCP/IP-based server.
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2003,2004,2005,2006,2007,2008 Olly Betts
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
00021  */
00022 
00023 #include <config.h>
00024 
00025 #include "tcpserver.h"
00026 
00027 #include <xapian/error.h>
00028 
00029 #include "safeerrno.h"
00030 #include "safefcntl.h"
00031 
00032 #include "noreturn.h"
00033 #include "remoteserver.h"
00034 #include "utils.h"
00035 
00036 #ifdef __WIN32__
00037 # include <process.h>    /* _beginthread, _endthread */
00038 #else
00039 # include <sys/socket.h>
00040 # include <netinet/in_systm.h>
00041 # include <netinet/in.h>
00042 # include <netinet/ip.h>
00043 # include <netinet/tcp.h>
00044 # include <arpa/inet.h>
00045 # include <netdb.h>
00046 # include <signal.h>
00047 # include <sys/wait.h>
00048 #endif
00049 
00050 #include <iostream>
00051 
00052 #include <string.h>
00053 #include <sys/types.h>
00054 
00055 using namespace std;
00056 
00057 // Handle older systems.
00058 #if !defined SIGCHLD && defined SIGCLD
00059 # define SIGCHLD SIGCLD
00060 #endif
00061 
00062 #ifdef __WIN32__
00063 // We must call closesocket() (instead of just close()) under __WIN32__ or
00064 // else the socket remains in the CLOSE_WAIT state.
00065 # define CLOSESOCKET(S) closesocket(S)
00066 #else
00067 # define CLOSESOCKET(S) close(S)
00068 #endif
00069 
00071 TcpServer::TcpServer(const vector<std::string> &dbpaths_, const std::string & host, int port,
00072                      int msecs_active_timeout_,
00073                      int msecs_idle_timeout_,
00074                      bool writable_,
00075                      bool verbose_)
00076         : dbpaths(dbpaths_), writable(writable_),
00077 #if defined __CYGWIN__ || defined __WIN32__
00078           mutex(NULL),
00079 #endif
00080           listen_socket(get_listening_socket(host, port
00081 #if defined __CYGWIN__ || defined __WIN32__
00082                                              , mutex
00083 #endif
00084                                             )),
00085           msecs_active_timeout(msecs_active_timeout_),
00086           msecs_idle_timeout(msecs_idle_timeout_),
00087           verbose(verbose_)
00088 {
00089 }
00090 
00091 int
00092 TcpServer::get_listening_socket(const std::string & host, int port
00093 #if defined __CYGWIN__ || defined __WIN32__
00094                                 , HANDLE &mutex
00095 #endif
00096                                 )
00097 {
00098     int socketfd = socket(PF_INET, SOCK_STREAM, 0);
00099 
00100     if (socketfd < 0) {
00101         throw Xapian::NetworkError("socket", socket_errno());
00102     }
00103 
00104     int retval;
00105 
00106     {
00107         int optval = 1;
00108         // 4th argument might need to be void* or char* - cast it to char*
00109         // since C++ allows implicit conversion to void* but not from void*.
00110         retval = setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
00111                             reinterpret_cast<char *>(&optval),
00112                             sizeof(optval));
00113 
00114 #if defined __CYGWIN__ || defined __WIN32__
00115         // Windows has screwy semantics for SO_REUSEADDR - it allows the user
00116         // to bind to a port which is already bound and listening!  That's
00117         // just not suitable as we don't want multiple xapian-tcpsrv processes
00118         // listening on the same port, so we guard against that by using a named
00119         // win32 mutex object (and we create it in the 'Global namespace' so
00120         // that this still works in a Terminal Services environment).
00121         char name[64];
00122         sprintf(name, "Global\\xapian-tcpserver-listening-%d", port);
00123         if ((mutex = CreateMutex(NULL, TRUE, name)) == NULL) {
00124             // We failed to create the mutex, probably the error is
00125             // ERROR_ACCESS_DENIED, which simply means that TcpServer is
00126             // already running on this port but as a different user.
00127         } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
00128             // The mutex already existed, so TcpServer is already running
00129             // on this port.
00130             CloseHandle(mutex);
00131             mutex = NULL;
00132         }
00133         if (mutex == NULL) {
00134             cerr << "xapian-tcpsrv is already running on port " << port << endl;
00135             // 69 is EX_UNAVAILABLE.  Scripts can use this to detect if
00136             // xapian-tcpsrv failed to bind to the requested port.
00137             exit(69); // FIXME: calling exit() here isn't ideal...
00138         }
00139 #endif
00140         if (retval >= 0) {
00141             retval = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,
00142                                 reinterpret_cast<char *>(&optval),
00143                                 sizeof(optval));
00144         }
00145 
00146 #if defined SO_EXCLUSIVEADDRUSE
00147         // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
00148         // security issues from SO_REUSEADDR (which affect *any* listening
00149         // process, even if doesn't use SO_REUSEADDR itself).  There's still no
00150         // way of addressing the issue of not being able to listen on a port
00151         // which has closed connections in TIME_WAIT state though.
00152         //
00153         // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
00154         // Because of this and the lack support on older versions, we don't
00155         // currently check the return value.
00156         if (retval >= 0) {
00157             (void)setsockopt(socketfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
00158                              reinterpret_cast<char *>(&optval),
00159                              sizeof(optval));
00160         }
00161 #endif
00162     }
00163 
00164     if (retval < 0) {
00165         int saved_errno = socket_errno(); // note down in case close hits an error
00166         CLOSESOCKET(socketfd);
00167         throw Xapian::NetworkError("setsockopt failed", saved_errno);
00168     }
00169 
00170     struct sockaddr_in addr;
00171     addr.sin_family = AF_INET;
00172     addr.sin_port = htons(port);
00173     if (host.empty()) {
00174         addr.sin_addr.s_addr = INADDR_ANY;
00175     } else {
00176         // FIXME: timeout on gethostbyname() ?
00177         struct hostent *hostent = gethostbyname(host.c_str());
00178 
00179         if (hostent == 0) {
00180             throw Xapian::NetworkError(string("Couldn't resolve host ") + host,
00181                 "",
00182 #ifdef __WIN32__
00183                 socket_errno()
00184 #else
00185                 // "socket_errno()" is just errno on UNIX which is
00186                 // inappropriate here - if gethostbyname() returns NULL an
00187                 // error code is available in h_errno (with values
00188                 // incompatible with errno).  On Linux at least, if h_errno
00189                 // is < 0, then the error code *IS* in errno!
00190                 (h_errno < 0 ? errno : -h_errno)
00191 #endif
00192                 );
00193         }
00194 
00195         memcpy(&addr.sin_addr, hostent->h_addr, sizeof(addr.sin_addr));
00196     }
00197 
00198     retval = bind(socketfd,
00199                   reinterpret_cast<sockaddr *>(&addr),
00200                   sizeof(addr));
00201 
00202     if (retval < 0) {
00203         int saved_errno = socket_errno(); // note down in case close hits an error
00204         if (saved_errno == EADDRINUSE) {
00205             cerr << host << ':' << port << " already in use" << endl;
00206             // 69 is EX_UNAVAILABLE.  Scripts can use this to detect if
00207             // xapian-tcpsrv failed to bind to the requested port.
00208             exit(69); // FIXME: calling exit() here isn't ideal...
00209         }
00210         CLOSESOCKET(socketfd);
00211         throw Xapian::NetworkError("bind failed", saved_errno);
00212     }
00213 
00214     retval = listen(socketfd, 5);
00215 
00216     if (retval < 0) {
00217         int saved_errno = socket_errno(); // note down in case close hits an error
00218         CLOSESOCKET(socketfd);
00219         throw Xapian::NetworkError("listen failed", saved_errno);
00220     }
00221     return socketfd;
00222 }
00223 
00224 int
00225 TcpServer::accept_connection()
00226 {
00227     struct sockaddr_in remote_address;
00228     SOCKLEN_T remote_address_size = sizeof(remote_address);
00229     // accept connections
00230     int con_socket = accept(listen_socket,
00231                             reinterpret_cast<sockaddr *>(&remote_address),
00232                             &remote_address_size);
00233 
00234     if (con_socket < 0) {
00235 #ifdef __WIN32__
00236         if (WSAGetLastError() == WSAEINTR) {
00237             // Our CtrlHandler function closed the socket.
00238             if (mutex) CloseHandle(mutex);
00239             mutex = NULL;
00240             return -1;
00241         }
00242 #endif
00243         throw Xapian::NetworkError("accept failed", socket_errno());
00244     }
00245 
00246     if (remote_address_size != sizeof(remote_address)) {
00247         throw Xapian::NetworkError("accept: unexpected remote address size");
00248     }
00249 
00250     if (verbose) {
00251         cout << "Connection from " << inet_ntoa(remote_address.sin_addr)
00252              << ", port " << remote_address.sin_port << endl;
00253     }
00254 
00255     return con_socket;
00256 }
00257 
00258 TcpServer::~TcpServer()
00259 {
00260     CLOSESOCKET(listen_socket);
00261 #if defined __CYGWIN__ || defined __WIN32__
00262     if (mutex) CloseHandle(mutex);
00263 #endif
00264 }
00265 
00266 void
00267 TcpServer::handle_one_connection(int socket)
00268 {
00269     try {
00270         RemoteServer sserv(dbpaths, socket, socket,
00271                            msecs_active_timeout, msecs_idle_timeout,
00272                            writable);
00273         sserv.run();
00274         CLOSESOCKET(socket);
00275     } catch (const Xapian::NetworkTimeoutError &e) {
00276         CLOSESOCKET(socket);
00277         if (verbose)
00278             cerr << "Connection timed out: " << e.get_description() << endl;
00279     } catch (const Xapian::Error &e) {
00280         CLOSESOCKET(socket);
00281         cerr << "Got exception " << e.get_description() << endl;
00282     } catch (...) {
00283         CLOSESOCKET(socket);
00284         // ignore other exceptions
00285     }
00286 }
00287 
00288 #ifdef HAVE_FORK
00289 // A fork() based implementation.
00290 void
00291 TcpServer::run_once()
00292 {
00293     int connected_socket = accept_connection();
00294     pid_t pid = fork();
00295     if (pid == 0) {
00296         // Child process.
00297         close(listen_socket);
00298 
00299         handle_one_connection(connected_socket);
00300 
00301         if (verbose) cout << "Closing connection." << endl;
00302         exit(0);
00303     }
00304 
00305     // Parent process.
00306 
00307     if (pid < 0) {
00308         // fork() failed
00309         int saved_errno = socket_errno(); // note down in case close hits an error
00310         close(connected_socket);
00311         throw Xapian::NetworkError("fork failed", saved_errno);
00312     }
00313 
00314     close(connected_socket);
00315 }
00316 
00317 extern "C" {
00318 
00319 XAPIAN_NORETURN(static void on_SIGTERM(int /*sig*/));
00320 
00321 static void
00322 on_SIGTERM(int /*sig*/)
00323 {
00324     signal(SIGTERM, SIG_DFL);
00325     /* terminate all processes in my process group */
00326 #ifdef HAVE_KILLPG
00327     killpg(0, SIGTERM);
00328 #else
00329     kill(0, SIGTERM);
00330 #endif
00331     exit(0);
00332 }
00333 
00334 #ifdef HAVE_WAITPID
00335 static void
00336 on_SIGCHLD(int /*sig*/)
00337 {
00338     int status;
00339     while (waitpid(-1, &status, WNOHANG) > 0);
00340 }
00341 #endif
00342 
00343 }
00344 
00345 void
00346 TcpServer::run()
00347 {
00348     // Handle connections until shutdown.
00349 
00350     // Set up signal handlers.
00351 #ifdef HAVE_WAITPID
00352     signal(SIGCHLD, on_SIGCHLD);
00353 #else
00354     signal(SIGCHLD, SIG_IGN);
00355 #endif
00356     signal(SIGTERM, on_SIGTERM);
00357 
00358     while (true) {
00359         try {
00360             run_once();
00361         } catch (const Xapian::Error &e) {
00362             // FIXME: better error handling.
00363             cerr << "Caught " << e.get_description() << endl;
00364         } catch (...) {
00365             // FIXME: better error handling.
00366             cerr << "Caught exception." << endl;
00367         }
00368     }
00369 }
00370 
00371 #elif defined __WIN32__
00372 
00373 // A threaded, Windows specific, implementation.
00374 
00379 static const int *pShutdownSocket = NULL;
00380 
00382 static BOOL
00383 CtrlHandler(DWORD fdwCtrlType)
00384 {
00385     switch (fdwCtrlType) {
00386         case CTRL_C_EVENT:
00387         case CTRL_CLOSE_EVENT:
00388             //  Console is about to die.
00389             // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
00390             // confirmation dialog asking if we really are sure.
00391         case CTRL_LOGOFF_EVENT:
00392         case CTRL_SHUTDOWN_EVENT:
00393             // These 2 will probably need to change when we get service
00394             // support - the service will prevent these being seen, so only
00395             // apply interactively.
00396             cout << "Shutting down..." << endl;
00397             break; // default behaviour
00398         case CTRL_BREAK_EVENT:
00399             // This (probably) means the developer is struggling to get
00400             // things to behave, and really wants to shutdown so let the OS
00401             // handle Ctrl+Break in the default way.
00402             cout << "Ctrl+Break: aborting process" << endl;
00403             return FALSE;
00404         default:
00405             cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
00406             return FALSE;
00407     }
00408 
00409     // Note: close() does not cause a blocking accept() call to terminate.
00410     // However, it appears closesocket() does.  This is much easier than trying
00411     // to setup a non-blocking accept().
00412     if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
00413        // We failed to close the socket, so just let the OS handle the
00414        // event in the default way.
00415        return FALSE;
00416     }
00417 
00418     pShutdownSocket = NULL;
00419     return TRUE; // Tell the OS that we've handled the event.
00420 }
00421 
00423 struct thread_param
00424 {
00425     thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
00426     TcpServer *server;
00427     int connected_socket;
00428 };
00429 
00431 static unsigned __stdcall
00432 run_thread(void * param_)
00433 {
00434     thread_param * param(reinterpret_cast<thread_param *>(param_));
00435     int socket = param->connected_socket;
00436 
00437     param->server->handle_one_connection(socket);
00438 
00439     delete param;
00440 
00441     _endthreadex(0);
00442     return 0;
00443 }
00444 
00445 void
00446 TcpServer::run()
00447 {
00448     // Handle connections until shutdown.
00449 
00450     // Set up the shutdown handler - this is a bit hacky, and sadly involves
00451     // a global variable.
00452     pShutdownSocket = &listen_socket;
00453     if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
00454         throw Xapian::NetworkError("Failed to install shutdown handler");
00455 
00456     while (true) {
00457         try {
00458             int connected_socket = accept_connection();
00459             if (connected_socket == -1)
00460                return; // Shutdown has happened
00461 
00462             // Spawn a new thread to handle the connection.
00463             // (This seems like lots of hoops just to end up calling
00464             // this->handle_one_connection() on a new thread. There might be a
00465             // better way...)
00466             thread_param *param = new thread_param(this, connected_socket);
00467             HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
00468             if (hthread == 0) {
00469                // errno holds the error code from _beginthreadex, and
00470                // closesocket() doesn't set errno.
00471                closesocket(connected_socket);
00472                throw Xapian::NetworkError("_beginthreadex failed", errno);
00473             }
00474 
00475             // FIXME: keep track of open thread handles so we can gracefully
00476             // close each thread down.  OTOH, when we want to kill them all its
00477             // likely to mean the process is on its way down, so it doesn't
00478             // really matter...
00479             CloseHandle(hthread);
00480         } catch (const Xapian::Error &e) {
00481             // FIXME: better error handling.
00482             cerr << "Caught " << e.get_description() << endl;
00483         } catch (...) {
00484             // FIXME: better error handling.
00485             cerr << "Caught exception." << endl;
00486         }
00487     }
00488 }
00489 
00490 void
00491 TcpServer::run_once()
00492 {
00493     // Run a single request on the current thread.
00494     handle_one_connection(accept_connection());
00495 }
00496 
00497 #else
00498 # error Neither HAVE_FORK nor __WIN32__ are defined.
00499 #endif

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.