xapian-core  1.4.27
tcpserver.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2015,2017,2018 Olly Betts
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23 
24 #include <config.h>
25 
26 #include "tcpserver.h"
27 
28 #include <xapian/error.h>
29 
30 #include "safefcntl.h"
31 #include "safenetdb.h"
32 #include "safesysexits.h"
33 #include "safesyssocket.h"
34 
35 #include "noreturn.h"
36 #include "remoteconnection.h"
37 #include "resolver.h"
38 #include "str.h"
39 
40 #ifdef __WIN32__
41 # include <process.h> /* _beginthreadex, _endthreadex */
42 #else
43 # include <netinet/in_systm.h>
44 # include <netinet/in.h>
45 # include <netinet/ip.h>
46 # include <netinet/tcp.h>
47 # include <arpa/inet.h>
48 # include <signal.h>
49 # include <sys/wait.h>
50 #endif
51 
52 #include <iostream>
53 
54 #include <cerrno>
55 #include <cstring>
56 #include <cstdlib>
57 #include <sys/types.h>
58 
59 using namespace std;
60 
61 // Handle older systems.
62 #if !defined SIGCHLD && defined SIGCLD
63 # define SIGCHLD SIGCLD
64 #endif
65 
66 #ifdef __WIN32__
67 // We must call closesocket() (instead of just close()) under __WIN32__ or
68 // else the socket remains in the CLOSE_WAIT state.
69 # define CLOSESOCKET(S) closesocket(S)
70 #else
71 # define CLOSESOCKET(S) close(S)
72 #endif
73 
75 TcpServer::TcpServer(const std::string & host, int port, bool tcp_nodelay,
76  bool verbose_)
77  : listen_socket(get_listening_socket(host, port, tcp_nodelay
78 #if defined __CYGWIN__ || defined __WIN32__
79  , mutex
80 #endif
81  )),
82  verbose(verbose_)
83 {
84 }
85 
86 int
87 TcpServer::get_listening_socket(const std::string & host, int port,
88  bool tcp_nodelay
89 #if defined __CYGWIN__ || defined __WIN32__
90  , HANDLE &mutex
91 #endif
92  )
93 {
94  int socketfd = -1;
95  int bind_errno = 0;
96  for (auto&& r : Resolver(host, port, AI_PASSIVE)) {
97  int socktype = r.ai_socktype | SOCK_CLOEXEC;
98  int fd = socket(r.ai_family, socktype, r.ai_protocol);
99  if (fd == -1)
100  continue;
101 
102 #if !defined __WIN32__ && defined F_SETFD && defined FD_CLOEXEC
103  // We can't use a preprocessor check on the *value* of SOCK_CLOEXEC as on
104  // Linux SOCK_CLOEXEC is an enum, with '#define SOCK_CLOEXEC SOCK_CLOEXEC'
105  // to allow '#ifdef SOCK_CLOEXEC' to work.
106  if (SOCK_CLOEXEC == 0)
107  (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
108 #endif
109 
110  int retval = 0;
111 
112  if (tcp_nodelay) {
113  int optval = 1;
114  // 4th argument might need to be void* or char* - cast it to char*
115  // since C++ allows implicit conversion to void* but not from
116  // void*.
117  retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
118  reinterpret_cast<char *>(&optval),
119  sizeof(optval));
120  }
121 
122  int optval = 1;
123 #if defined __CYGWIN__ || defined __WIN32__
124  // Windows has screwy semantics for SO_REUSEADDR - it allows the user
125  // to bind to a port which is already bound and listening! That's
126  // just not suitable as we don't want multiple processes listening on
127  // the same port, so we guard against that by using a named win32 mutex
128  // object (and we create it in the 'Global namespace' so that this
129  // still works in a Terminal Services environment).
130  string name = "Global\\xapian-tcpserver-listening-" + str(port);
131  if ((mutex = CreateMutex(NULL, TRUE, name.c_str())) == NULL) {
132  // We failed to create the mutex, probably the error is
133  // ERROR_ACCESS_DENIED, which simply means that TcpServer is
134  // already running on this port but as a different user.
135  } else if (GetLastError() == ERROR_ALREADY_EXISTS) {
136  // The mutex already existed, so TcpServer is already running
137  // on this port.
138  CloseHandle(mutex);
139  mutex = NULL;
140  }
141  if (mutex == NULL) {
142  cerr << "Server is already running on port " << port << endl;
143  // 69 is EX_UNAVAILABLE. Scripts can use this to detect if the
144  // server failed to bind to the requested port.
145  exit(69); // FIXME: calling exit() here isn't ideal...
146  }
147 #endif
148  if (retval >= 0) {
149  retval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
150  reinterpret_cast<char *>(&optval),
151  sizeof(optval));
152  }
153 
154  if (retval < 0) {
155  int saved_errno = socket_errno(); // note down in case close hits an error
156  CLOSESOCKET(fd);
157  throw Xapian::NetworkError("setsockopt failed", saved_errno);
158  }
159 
160  if (::bind(fd, r.ai_addr, r.ai_addrlen) == 0) {
161  socketfd = fd;
162  break;
163  }
164 
165  // Note down the error code for the first address we try, which seems
166  // likely to be more helpful than the last in the case where they
167  // differ.
168  if (bind_errno == 0)
169  bind_errno = socket_errno();
170 
171  CLOSESOCKET(fd);
172  }
173 
174  if (socketfd == -1) {
175  if (bind_errno == EADDRINUSE) {
176  cerr << host << ':' << port << " already in use" << endl;
177  // EX_UNAVAILABLE is 69. Scripts can use this to detect that the
178  // requested port was already in use.
179  exit(EX_UNAVAILABLE);
180  }
181  if (bind_errno == EACCES) {
182  cerr << "Can't bind to privileged port " << port << endl;
183  // EX_NOPERM is 77. Scripts can use this to detect if
184  // xapian-tcpsrv failed to bind to the requested port.
185  exit(EX_NOPERM);
186  }
187  throw Xapian::NetworkError("bind failed", bind_errno);
188  }
189 
190  if (listen(socketfd, 5) < 0) {
191  int saved_errno = socket_errno(); // note down in case close hits an error
192  CLOSESOCKET(socketfd);
193  throw Xapian::NetworkError("listen failed", saved_errno);
194  }
195  return socketfd;
196 }
197 
198 int
199 TcpServer::accept_connection()
200 {
201  struct sockaddr_in remote_address;
202  SOCKLEN_T remote_address_size = sizeof(remote_address);
203  // accept connections
204  int con_socket = accept(listen_socket,
205  reinterpret_cast<sockaddr *>(&remote_address),
206  &remote_address_size);
207 
208  if (con_socket < 0) {
209 #ifdef __WIN32__
210  if (WSAGetLastError() == WSAEINTR) {
211  // Our CtrlHandler function closed the socket.
212  if (mutex) CloseHandle(mutex);
213  mutex = NULL;
214  return -1;
215  }
216 #endif
217  throw Xapian::NetworkError("accept failed", socket_errno());
218  }
219 
220  if (remote_address_size != sizeof(remote_address)) {
221  throw Xapian::NetworkError("accept: unexpected remote address size");
222  }
223 
224  if (verbose) {
225  char buf[INET_ADDRSTRLEN];
226 #ifndef __WIN32__
227  // Under __WIN32__, inet_ntop()'s second parameter isn't const for some
228  // reason. We don't currently use inet_ntop() there, but allow for a
229  // non-const second parameter in case it's more widespread.
230  void * src = &remote_address.sin_addr;
231  const char * r = inet_ntop(AF_INET, src, buf, sizeof(buf));
232  if (!r)
233  throw Xapian::NetworkError("inet_ntop failed", errno);
234 #else
235  // inet_ntop() isn't always available, at least with mingw.
236  // WSAAddressToString() supports both IPv4 and IPv6, so just use that.
237  DWORD size = sizeof(buf);
238  if (WSAAddressToString(reinterpret_cast<sockaddr*>(&remote_address),
239  sizeof(remote_address), NULL, buf, &size) != 0) {
240  throw Xapian::NetworkError("WSAAddressToString failed",
241  WSAGetLastError());
242  }
243  const char * r = buf;
244 #endif
245  int port = remote_address.sin_port;
246  cout << "Connection from " << r << ", port " << port << endl;
247  }
248 
249  return con_socket;
250 }
251 
252 TcpServer::~TcpServer()
253 {
254  CLOSESOCKET(listen_socket);
255 #if defined __CYGWIN__ || defined __WIN32__
256  if (mutex) CloseHandle(mutex);
257 #endif
258 }
259 
260 #ifdef HAVE_FORK
261 // A fork() based implementation.
262 
263 extern "C" {
264 
265 XAPIAN_NORETURN(static void on_SIGTERM(int /*sig*/));
266 
267 static void
268 on_SIGTERM(int /*sig*/)
269 {
270  signal(SIGTERM, SIG_DFL);
271  /* terminate all processes in my process group */
272 #ifdef HAVE_KILLPG
273  killpg(0, SIGTERM);
274 #else
275  kill(0, SIGTERM);
276 #endif
277  exit(0);
278 }
279 
280 #ifdef HAVE_WAITPID
281 static void
282 on_SIGCHLD(int /*sig*/)
283 {
284  int status;
285  while (waitpid(-1, &status, WNOHANG) > 0);
286 }
287 #endif
288 
289 }
290 
291 void
292 TcpServer::run()
293 {
294  // Handle connections until shutdown.
295 
296  // Set up signal handlers.
297 #ifdef HAVE_WAITPID
298  signal(SIGCHLD, on_SIGCHLD);
299 #else
300  signal(SIGCHLD, SIG_IGN);
301 #endif
302  signal(SIGTERM, on_SIGTERM);
303 
304  while (true) {
305  try {
306  int connected_socket = accept_connection();
307  pid_t pid = fork();
308  if (pid == 0) {
309  // Child process.
310  close(listen_socket);
311 
312  handle_one_connection(connected_socket);
313  close(connected_socket);
314 
315  if (verbose) cout << "Connection closed." << endl;
316  _exit(0);
317  }
318 
319  // Parent process.
320 
321  if (pid < 0) {
322  // fork() failed.
323 
324  // Note down errno from fork() in case close() hits an error.
325  int saved_errno = socket_errno();
326  close(connected_socket);
327  throw Xapian::NetworkError("fork failed", saved_errno);
328  }
329 
330  close(connected_socket);
331  } catch (const Xapian::Error &e) {
332  // FIXME: better error handling.
333  cerr << "Caught " << e.get_description() << endl;
334  } catch (...) {
335  // FIXME: better error handling.
336  cerr << "Caught exception." << endl;
337  }
338  }
339 }
340 
341 #elif defined __WIN32__
342 
343 // A threaded, Windows specific, implementation.
344 
349 static const int *pShutdownSocket = NULL;
350 
352 static BOOL
353 CtrlHandler(DWORD fdwCtrlType)
354 {
355  switch (fdwCtrlType) {
356  case CTRL_C_EVENT:
357  case CTRL_CLOSE_EVENT:
358  // Console is about to die.
359  // CTRL_CLOSE_EVENT gives us 5 seconds before displaying a
360  // confirmation dialog asking if we really are sure.
361  case CTRL_LOGOFF_EVENT:
362  case CTRL_SHUTDOWN_EVENT:
363  // These 2 will probably need to change when we get service
364  // support - the service will prevent these being seen, so only
365  // apply interactively.
366  cout << "Shutting down..." << endl;
367  break; // default behaviour
368  case CTRL_BREAK_EVENT:
369  // This (probably) means the developer is struggling to get
370  // things to behave, and really wants to shutdown so let the OS
371  // handle Ctrl+Break in the default way.
372  cout << "Ctrl+Break: aborting process" << endl;
373  return FALSE;
374  default:
375  cerr << "unexpected CtrlHandler: " << fdwCtrlType << endl;
376  return FALSE;
377  }
378 
379  // Note: close() does not cause a blocking accept() call to terminate.
380  // However, it appears closesocket() does. This is much easier than trying
381  // to setup a non-blocking accept().
382  if (!pShutdownSocket || closesocket(*pShutdownSocket) == SOCKET_ERROR) {
383  // We failed to close the socket, so just let the OS handle the
384  // event in the default way.
385  return FALSE;
386  }
387 
388  pShutdownSocket = NULL;
389  return TRUE; // Tell the OS that we've handled the event.
390 }
391 
393 struct thread_param
394 {
395  thread_param(TcpServer *s, int c) : server(s), connected_socket(c) {}
396  TcpServer *server;
397  int connected_socket;
398 };
399 
401 static unsigned __stdcall
402 run_thread(void * param_)
403 {
404  thread_param * param(reinterpret_cast<thread_param *>(param_));
405  int socket = param->connected_socket;
406 
407  param->server->handle_one_connection(socket);
408  closesocket(socket);
409 
410  delete param;
411 
412  _endthreadex(0);
413  return 0;
414 }
415 
416 void
417 TcpServer::run()
418 {
419  // Handle connections until shutdown.
420 
421  // Set up the shutdown handler - this is a bit hacky, and sadly involves
422  // a global variable.
423  pShutdownSocket = &listen_socket;
424  if (!::SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))
425  throw Xapian::NetworkError("Failed to install shutdown handler");
426 
427  while (true) {
428  try {
429  int connected_socket = accept_connection();
430  if (connected_socket == -1)
431  return; // Shutdown has happened
432 
433  // Spawn a new thread to handle the connection.
434  // (This seems like lots of hoops just to end up calling
435  // this->handle_one_connection() on a new thread. There might be a
436  // better way...)
437  thread_param *param = new thread_param(this, connected_socket);
438  HANDLE hthread = (HANDLE)_beginthreadex(NULL, 0, ::run_thread, param, 0, NULL);
439  if (hthread == 0) {
440  // errno holds the error code from _beginthreadex, and
441  // closesocket() doesn't set errno.
442  closesocket(connected_socket);
443  throw Xapian::NetworkError("_beginthreadex failed", errno);
444  }
445 
446  // FIXME: keep track of open thread handles so we can gracefully
447  // close each thread down. OTOH, when we want to kill them all its
448  // likely to mean the process is on its way down, so it doesn't
449  // really matter...
450  CloseHandle(hthread);
451  } catch (const Xapian::Error &e) {
452  // FIXME: better error handling.
453  cerr << "Caught " << e.get_description() << endl;
454  } catch (...) {
455  // FIXME: better error handling.
456  cerr << "Caught exception." << endl;
457  }
458  }
459 }
460 
461 #else
462 # error Neither HAVE_FORK nor __WIN32__ are defined.
463 #endif
464 
465 void
466 TcpServer::run_once()
467 {
468  // Run a single request in the current process/thread.
469  int fd = accept_connection();
470  handle_one_connection(fd);
471  CLOSESOCKET(fd);
472 }
int close(FD &fd)
Definition: fd.h:63
Define the XAPIAN_NORETURN macro.
RemoteConnection class used by the remote backend.
#define CLOSESOCKET(S)
Definition: tcpserver.cc:71
include <sys/socket.h> with portability workarounds.
#define SOCKLEN_T
Definition: config.h:371
STL namespace.
Convert types to std::string.
include <netdb.h>, with portability workarounds.
#define SOCK_CLOEXEC
Definition: safesyssocket.h:83
Hierarchy of classes which Xapian can throw as exceptions.
static int verbose
Definition: xapian-delve.cc:47
include <sysexits.h> with portability workarounds.
Resolve hostnames and ip addresses.
#define EX_NOPERM
Definition: safesysexits.h:42
string str(int value)
Convert int to std::string.
Definition: str.cc:90
std::string get_description() const
Return a string describing this object.
Definition: error.cc:93
#define EX_UNAVAILABLE
Definition: safesysexits.h:33
All exceptions thrown by Xapian are subclasses of Xapian::Error.
Definition: error.h:43
Indicates a problem communicating with a remote database.
Definition: error.h:803
Definition: header.h:151
Generic TCP/IP socket based server base class.
if(!(properties &BACKEND))
Definition: api_collated.h:3
int socket_errno()
include <fcntl.h>, but working around broken platforms.