00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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>
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
00058 #if !defined SIGCHLD && defined SIGCLD
00059 # define SIGCHLD SIGCLD
00060 #endif
00061
00062 #ifdef __WIN32__
00063
00064
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
00109
00110 retval = setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
00111 reinterpret_cast<char *>(&optval),
00112 sizeof(optval));
00113
00114 #if defined __CYGWIN__ || defined __WIN32__
00115
00116
00117
00118
00119
00120
00121 char name[64];
00122 sprintf(name, "Global\\xapian-tcpserver-listening-%d", port);
00123 if ((mutex = CreateMutex(NULL, TRUE, name)) == NULL) {
00124
00125
00126
00127 } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
00128
00129
00130 CloseHandle(mutex);
00131 mutex = NULL;
00132 }
00133 if (mutex == NULL) {
00134 cerr << "xapian-tcpsrv is already running on port " << port << endl;
00135
00136
00137 exit(69);
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
00148
00149
00150
00151
00152
00153
00154
00155
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();
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
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
00186
00187
00188
00189
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();
00204 if (saved_errno == EADDRINUSE) {
00205 cerr << host << ':' << port << " already in use" << endl;
00206
00207
00208 exit(69);
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();
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
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
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
00285 }
00286 }
00287
00288 #ifdef HAVE_FORK
00289
00290 void
00291 TcpServer::run_once()
00292 {
00293 int connected_socket = accept_connection();
00294 pid_t pid = fork();
00295 if (pid == 0) {
00296
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
00306
00307 if (pid < 0) {
00308
00309 int saved_errno = socket_errno();
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 ));
00320
00321 static void
00322 on_SIGTERM(int )
00323 {
00324 signal(SIGTERM, SIG_DFL);
00325
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 )
00337 {
00338 int status;
00339 while (waitpid(-1, &status, WNOHANG) > 0);
00340 }
00341 #endif
00342
00343 }
00344
00345 void
00346 TcpServer::run()
00347 {
00348
00349
00350
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
00363 cerr << "Caught " << e.get_description() << endl;
00364 } catch (...) {
00365
00366 cerr << "Caught exception." << endl;
00367 }
00368 }
00369 }
00370
00371 #elif defined __WIN32__
00372
00373
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
00389
00390
00391 case CTRL_LOGOFF_EVENT:
00392 case CTRL_SHUTDOWN_EVENT:
00393
00394
00395
00396 cout << "Shutting down..." << endl;
00397 break;
00398 case CTRL_BREAK_EVENT:
00399
00400
00401
00402 cout << "Ctrl+Break: aborting process" << endl;
00403 return FALSE;
00404 default:
00405 cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
00406 return FALSE;
00407 }
00408
00409
00410
00411
00412 if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
00413
00414
00415 return FALSE;
00416 }
00417
00418 pShutdownSocket = NULL;
00419 return TRUE;
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
00449
00450
00451
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;
00461
00462
00463
00464
00465
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
00470
00471 closesocket(connected_socket);
00472 throw Xapian::NetworkError("_beginthreadex failed", errno);
00473 }
00474
00475
00476
00477
00478
00479 CloseHandle(hthread);
00480 } catch (const Xapian::Error &e) {
00481
00482 cerr << "Caught " << e.get_description() << endl;
00483 } catch (...) {
00484
00485 cerr << "Caught exception." << endl;
00486 }
00487 }
00488 }
00489
00490 void
00491 TcpServer::run_once()
00492 {
00493
00494 handle_one_connection(accept_connection());
00495 }
00496
00497 #else
00498 # error Neither HAVE_FORK nor __WIN32__ are defined.
00499 #endif