Branch data Line data Source code
1 : : /* tcpserver.cc: class for TCP/IP-based server.
2 : : *
3 : : * Copyright 1999,2000,2001 BrightStation PLC
4 : : * Copyright 2002 Ananova Ltd
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011 Olly Betts
6 : : *
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.
11 : : *
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.
16 : : *
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
21 : : */
22 : :
23 : : #include <config.h>
24 : :
25 : : #include "tcpserver.h"
26 : :
27 : : #include <xapian/error.h>
28 : :
29 : : #include "safeerrno.h"
30 : : #include "safefcntl.h"
31 : :
32 : : #include "noreturn.h"
33 : : #include "remoteconnection.h"
34 : : #include "utils.h"
35 : :
36 : : #ifdef __WIN32__
37 : : # include <process.h> /* _beginthread, _endthread */
38 : : #else
39 : : # include <sys/socket.h>
40 : : # include <netinet/in_systm.h>
41 : : # include <netinet/in.h>
42 : : # include <netinet/ip.h>
43 : : # include <netinet/tcp.h>
44 : : # include <arpa/inet.h>
45 : : # include <netdb.h>
46 : : # include <signal.h>
47 : : # include <sys/wait.h>
48 : : #endif
49 : :
50 : : #include <iostream>
51 : :
52 : : #include <cstring>
53 : : #include <cstdio> // For sprintf() on __WIN32__ or cygwin.
54 : : #include <sys/types.h>
55 : :
56 : : using namespace std;
57 : :
58 : : // Handle older systems.
59 : : #if !defined SIGCHLD && defined SIGCLD
60 : : # define SIGCHLD SIGCLD
61 : : #endif
62 : :
63 : : #ifdef __WIN32__
64 : : // We must call closesocket() (instead of just close()) under __WIN32__ or
65 : : // else the socket remains in the CLOSE_WAIT state.
66 : : # define CLOSESOCKET(S) closesocket(S)
67 : : #else
68 : : # define CLOSESOCKET(S) close(S)
69 : : #endif
70 : :
71 : : /// The TcpServer constructor, taking a database and a listening port.
72 : 1542 : TcpServer::TcpServer(const std::string & host, int port, bool tcp_nodelay,
73 : : bool verbose_)
74 : : :
75 : : #if defined __CYGWIN__ || defined __WIN32__
76 : : mutex(NULL),
77 : : #endif
78 : : listen_socket(get_listening_socket(host, port, tcp_nodelay
79 : : #if defined __CYGWIN__ || defined __WIN32__
80 : : , mutex
81 : : #endif
82 : : )),
83 : 1542 : verbose(verbose_)
84 : : {
85 : 1542 : }
86 : :
87 : : int
88 : 1542 : TcpServer::get_listening_socket(const std::string & host, int port,
89 : : bool tcp_nodelay
90 : : #if defined __CYGWIN__ || defined __WIN32__
91 : : , HANDLE &mutex
92 : : #endif
93 : : )
94 : : {
95 : 1542 : int socketfd = socket(PF_INET, SOCK_STREAM, 0);
96 : :
97 [ - + ]: 1542 : if (socketfd < 0) {
98 : 0 : throw Xapian::NetworkError("socket", socket_errno());
99 : : }
100 : :
101 : 1542 : int retval = 0;
102 : :
103 [ + - ]: 1542 : if (tcp_nodelay) {
104 : 1542 : int optval = 1;
105 : : // 4th argument might need to be void* or char* - cast it to char*
106 : : // since C++ allows implicit conversion to void* but not from void*.
107 : : retval = setsockopt(socketfd, IPPROTO_TCP, TCP_NODELAY,
108 : : reinterpret_cast<char *>(&optval),
109 : 1542 : sizeof(optval));
110 : : }
111 : :
112 : : {
113 : 1542 : int optval = 1;
114 : : #if defined __CYGWIN__ || defined __WIN32__
115 : : // Windows has screwy semantics for SO_REUSEADDR - it allows the user
116 : : // to bind to a port which is already bound and listening! That's
117 : : // just not suitable as we don't want multiple processes listening on
118 : : // the same port, so we guard against that by using a named win32 mutex
119 : : // object (and we create it in the 'Global namespace' so that this
120 : : // still works in a Terminal Services environment).
121 : : char name[64];
122 : : sprintf(name, "Global\\xapian-tcpserver-listening-%d", port);
123 : : if ((mutex = CreateMutex(NULL, TRUE, name)) == NULL) {
124 : : // We failed to create the mutex, probably the error is
125 : : // ERROR_ACCESS_DENIED, which simply means that TcpServer is
126 : : // already running on this port but as a different user.
127 : : } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
128 : : // The mutex already existed, so TcpServer is already running
129 : : // on this port.
130 : : CloseHandle(mutex);
131 : : mutex = NULL;
132 : : }
133 : : if (mutex == NULL) {
134 : : cerr << "Server is already running on port " << port << endl;
135 : : // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
136 : : // server failed to bind to the requested port.
137 : : exit(69); // FIXME: calling exit() here isn't ideal...
138 : : }
139 : : #endif
140 [ + - ]: 1542 : if (retval >= 0) {
141 : : retval = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,
142 : : reinterpret_cast<char *>(&optval),
143 : 1542 : sizeof(optval));
144 : : }
145 : :
146 : : #if defined SO_EXCLUSIVEADDRUSE
147 : : // NT4 sp4 and later offer SO_EXCLUSIVEADDRUSE which nullifies the
148 : : // security issues from SO_REUSEADDR (which affect *any* listening
149 : : // process, even if doesn't use SO_REUSEADDR itself). There's still no
150 : : // way of addressing the issue of not being able to listen on a port
151 : : // which has closed connections in TIME_WAIT state though.
152 : : //
153 : : // Note: SO_EXCLUSIVEADDRUSE requires admin privileges prior to XP SP2.
154 : : // Because of this and the lack support on older versions, we don't
155 : : // currently check the return value.
156 : : if (retval >= 0) {
157 : : (void)setsockopt(socketfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
158 : : reinterpret_cast<char *>(&optval),
159 : : sizeof(optval));
160 : : }
161 : : #endif
162 : : }
163 : :
164 [ - + ]: 1542 : if (retval < 0) {
165 : 0 : int saved_errno = socket_errno(); // note down in case close hits an error
166 : 0 : CLOSESOCKET(socketfd);
167 : 0 : throw Xapian::NetworkError("setsockopt failed", saved_errno);
168 : : }
169 : :
170 : : struct sockaddr_in addr;
171 : 1542 : memset(&addr, 0, sizeof(addr));
172 : 1542 : addr.sin_family = AF_INET;
173 : 1542 : addr.sin_port = htons(port);
174 [ - + ]: 1542 : if (host.empty()) {
175 : 0 : addr.sin_addr.s_addr = INADDR_ANY;
176 : : } else {
177 : : // FIXME: timeout on gethostbyname() ?
178 : 1542 : struct hostent *hostent = gethostbyname(host.c_str());
179 : :
180 [ - + ]: 1542 : if (hostent == 0) {
181 : : throw Xapian::NetworkError(string("Couldn't resolve host ") + host,
182 : : "",
183 : : #ifdef __WIN32__
184 : : socket_errno()
185 : : #else
186 : : // "socket_errno()" is just errno on UNIX which is
187 : : // inappropriate here - if gethostbyname() returns NULL an
188 : : // error code is available in h_errno (with values
189 : : // incompatible with errno). On Linux at least, if h_errno
190 : : // is < 0, then the error code *IS* in errno!
191 : : (h_errno < 0 ? errno : -h_errno)
192 : : #endif
193 [ # # ]: 0 : );
194 : : }
195 : :
196 : 1542 : memcpy(&addr.sin_addr, hostent->h_addr, hostent->h_length);
197 : : }
198 : :
199 : : retval = bind(socketfd,
200 : : reinterpret_cast<sockaddr *>(&addr),
201 : 1542 : sizeof(addr));
202 : :
203 [ - + ]: 1542 : if (retval < 0) {
204 : 0 : int saved_errno = socket_errno(); // note down in case close hits an error
205 [ # # ]: 0 : if (saved_errno == EADDRINUSE) {
206 : 0 : cerr << host << ':' << port << " already in use" << endl;
207 : : // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
208 : : // server failed to bind to the requested port.
209 : 0 : exit(69); // FIXME: calling exit() here isn't ideal...
210 : : }
211 [ # # ]: 0 : if (saved_errno == EACCES) {
212 : 0 : cerr << "Can't bind to privileged port " << port << endl;
213 : : // 77 is EX_NOPERM. Scripts can use this to detect if
214 : : // xapian-tcpsrv failed to bind to the requested port.
215 : 0 : exit(77); // FIXME: calling exit() here isn't ideal...
216 : : }
217 : 0 : CLOSESOCKET(socketfd);
218 : 0 : throw Xapian::NetworkError("bind failed", saved_errno);
219 : : }
220 : :
221 : 1542 : retval = listen(socketfd, 5);
222 : :
223 [ - + ]: 1542 : if (retval < 0) {
224 : 0 : int saved_errno = socket_errno(); // note down in case close hits an error
225 : 0 : CLOSESOCKET(socketfd);
226 : 0 : throw Xapian::NetworkError("listen failed", saved_errno);
227 : : }
228 : 1542 : return socketfd;
229 : : }
230 : :
231 : : int
232 : 1542 : TcpServer::accept_connection()
233 : : {
234 : : struct sockaddr_in remote_address;
235 : 1542 : SOCKLEN_T remote_address_size = sizeof(remote_address);
236 : : // accept connections
237 : : int con_socket = accept(listen_socket,
238 : : reinterpret_cast<sockaddr *>(&remote_address),
239 : 1542 : &remote_address_size);
240 : :
241 [ - + ]: 1542 : if (con_socket < 0) {
242 : : #ifdef __WIN32__
243 : : if (WSAGetLastError() == WSAEINTR) {
244 : : // Our CtrlHandler function closed the socket.
245 : : if (mutex) CloseHandle(mutex);
246 : : mutex = NULL;
247 : : return -1;
248 : : }
249 : : #endif
250 : 0 : throw Xapian::NetworkError("accept failed", socket_errno());
251 : : }
252 : :
253 [ - + ]: 1542 : if (remote_address_size != sizeof(remote_address)) {
254 : 0 : throw Xapian::NetworkError("accept: unexpected remote address size");
255 : : }
256 : :
257 [ + - ]: 1542 : if (verbose) {
258 : : cout << "Connection from " << inet_ntoa(remote_address.sin_addr)
259 : 1542 : << ", port " << remote_address.sin_port << endl;
260 : : }
261 : :
262 : 1542 : return con_socket;
263 : : }
264 : :
265 : 771 : TcpServer::~TcpServer()
266 : : {
267 : 771 : CLOSESOCKET(listen_socket);
268 : : #if defined __CYGWIN__ || defined __WIN32__
269 : : if (mutex) CloseHandle(mutex);
270 : : #endif
271 [ # # ][ # # ]: 771 : }
[ - + ]
272 : :
273 : : #ifdef HAVE_FORK
274 : : // A fork() based implementation.
275 : : void
276 : 1542 : TcpServer::run_once()
277 : : {
278 : 1542 : int connected_socket = accept_connection();
279 : 1542 : pid_t pid = fork();
280 [ + + ]: 1542 : if (pid == 0) {
281 : : // Child process.
282 : 771 : close(listen_socket);
283 : :
284 : 771 : handle_one_connection(connected_socket);
285 : 771 : close(connected_socket);
286 : :
287 [ + - ]: 771 : if (verbose) cout << "Closing connection." << endl;
288 : 771 : exit(0);
289 : : }
290 : :
291 : : // Parent process.
292 : :
293 [ - + ]: 771 : if (pid < 0) {
294 : : // fork() failed
295 : 0 : int saved_errno = socket_errno(); // note down in case close hits an error
296 : 0 : close(connected_socket);
297 : 0 : throw Xapian::NetworkError("fork failed", saved_errno);
298 : : }
299 : :
300 : 771 : close(connected_socket);
301 : 771 : }
302 : :
303 : : extern "C" {
304 : :
305 : : XAPIAN_NORETURN(static void on_SIGTERM(int /*sig*/));
306 : :
307 : : static void
308 : 0 : on_SIGTERM(int /*sig*/)
309 : : {
310 : 0 : signal(SIGTERM, SIG_DFL);
311 : : /* terminate all processes in my process group */
312 : : #ifdef HAVE_KILLPG
313 : : killpg(0, SIGTERM);
314 : : #else
315 : 0 : kill(0, SIGTERM);
316 : : #endif
317 : 0 : exit(0);
318 : : }
319 : :
320 : : #ifdef HAVE_WAITPID
321 : : static void
322 : : on_SIGCHLD(int /*sig*/)
323 : : {
324 : : int status;
325 : : while (waitpid(-1, &status, WNOHANG) > 0);
326 : : }
327 : : #endif
328 : :
329 : : }
330 : :
331 : : void
332 : 0 : TcpServer::run()
333 : : {
334 : : // Handle connections until shutdown.
335 : :
336 : : // Set up signal handlers.
337 : : #ifdef HAVE_WAITPID
338 : : signal(SIGCHLD, on_SIGCHLD);
339 : : #else
340 : 0 : signal(SIGCHLD, SIG_IGN);
341 : : #endif
342 : 0 : signal(SIGTERM, on_SIGTERM);
343 : :
344 : 0 : while (true) {
345 : : try {
346 : 0 : run_once();
347 : 0 : } catch (const Xapian::Error &e) {
348 : : // FIXME: better error handling.
349 : 0 : cerr << "Caught " << e.get_description() << endl;
350 : 0 : } catch (...) {
351 : : // FIXME: better error handling.
352 : 0 : cerr << "Caught exception." << endl;
353 : : }
354 : : }
355 [ + - ][ + - ]: 4626 : }
356 : :
357 : : #elif defined __WIN32__
358 : :
359 : : // A threaded, Windows specific, implementation.
360 : :
361 : : /** The socket which will be closed by CtrlHandler.
362 : : *
363 : : * FIXME - is there any way to avoid using a global variable here?
364 : : */
365 : : static const int *pShutdownSocket = NULL;
366 : :
367 : : /// Console interrupt handler.
368 : : static BOOL
369 : : CtrlHandler(DWORD fdwCtrlType)
370 : : {
371 : : switch (fdwCtrlType) {
372 : : case CTRL_C_EVENT:
373 : : case CTRL_CLOSE_EVENT:
374 : : // Console is about to die.
375 : : // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
376 : : // confirmation dialog asking if we really are sure.
377 : : case CTRL_LOGOFF_EVENT:
378 : : case CTRL_SHUTDOWN_EVENT:
379 : : // These 2 will probably need to change when we get service
380 : : // support - the service will prevent these being seen, so only
381 : : // apply interactively.
382 : : cout << "Shutting down..." << endl;
383 : : break; // default behaviour
384 : : case CTRL_BREAK_EVENT:
385 : : // This (probably) means the developer is struggling to get
386 : : // things to behave, and really wants to shutdown so let the OS
387 : : // handle Ctrl+Break in the default way.
388 : : cout << "Ctrl+Break: aborting process" << endl;
389 : : return FALSE;
390 : : default:
391 : : cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
392 : : return FALSE;
393 : : }
394 : :
395 : : // Note: close() does not cause a blocking accept() call to terminate.
396 : : // However, it appears closesocket() does. This is much easier than trying
397 : : // to setup a non-blocking accept().
398 : : if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
399 : : // We failed to close the socket, so just let the OS handle the
400 : : // event in the default way.
401 : : return FALSE;
402 : : }
403 : :
404 : : pShutdownSocket = NULL;
405 : : return TRUE; // Tell the OS that we've handled the event.
406 : : }
407 : :
408 : : /// Structure which is used to pass parameters to the new threads.
409 : : struct thread_param
410 : : {
411 : : thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
412 : : TcpServer *server;
413 : : int connected_socket;
414 : : };
415 : :
416 : : /// The thread entry-point.
417 : : static unsigned __stdcall
418 : : run_thread(void * param_)
419 : : {
420 : : thread_param * param(reinterpret_cast<thread_param *>(param_));
421 : : int socket = param->connected_socket;
422 : :
423 : : param->server->handle_one_connection(socket);
424 : : closesocket(socket);
425 : :
426 : : delete param;
427 : :
428 : : _endthreadex(0);
429 : : return 0;
430 : : }
431 : :
432 : : void
433 : : TcpServer::run()
434 : : {
435 : : // Handle connections until shutdown.
436 : :
437 : : // Set up the shutdown handler - this is a bit hacky, and sadly involves
438 : : // a global variable.
439 : : pShutdownSocket = &listen_socket;
440 : : if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
441 : : throw Xapian::NetworkError("Failed to install shutdown handler");
442 : :
443 : : while (true) {
444 : : try {
445 : : int connected_socket = accept_connection();
446 : : if (connected_socket == -1)
447 : : return; // Shutdown has happened
448 : :
449 : : // Spawn a new thread to handle the connection.
450 : : // (This seems like lots of hoops just to end up calling
451 : : // this->handle_one_connection() on a new thread. There might be a
452 : : // better way...)
453 : : thread_param *param = new thread_param(this, connected_socket);
454 : : HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
455 : : if (hthread == 0) {
456 : : // errno holds the error code from _beginthreadex, and
457 : : // closesocket() doesn't set errno.
458 : : closesocket(connected_socket);
459 : : throw Xapian::NetworkError("_beginthreadex failed", errno);
460 : : }
461 : :
462 : : // FIXME: keep track of open thread handles so we can gracefully
463 : : // close each thread down. OTOH, when we want to kill them all its
464 : : // likely to mean the process is on its way down, so it doesn't
465 : : // really matter...
466 : : CloseHandle(hthread);
467 : : } catch (const Xapian::Error &e) {
468 : : // FIXME: better error handling.
469 : : cerr << "Caught " << e.get_description() << endl;
470 : : } catch (...) {
471 : : // FIXME: better error handling.
472 : : cerr << "Caught exception." << endl;
473 : : }
474 : : }
475 : : }
476 : :
477 : : void
478 : : TcpServer::run_once()
479 : : {
480 : : // Run a single request on the current thread.
481 : : int fd = accept_connection();
482 : : handle_one_connection(fd);
483 : : closesocket(fd);
484 : : }
485 : :
486 : : #else
487 : : # error Neither HAVE_FORK nor __WIN32__ are defined.
488 : : #endif
|