34 # include <sys/types.h> 36 # include <sys/wait.h> 39 # if !defined SIGCHLD && defined SIGCLD 40 # define SIGCHLD SIGCLD 59 # include <valgrind/memcheck.h> 67 #define LOCALHOST "127.0.0.1" 70 #define DEFAULT_PORT 1239 82 static pid_fd pid_to_fd[16];
91 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
92 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
93 if (pid_to_fd[i].pid == child) {
94 int fd = pid_to_fd[i].fd;
108 launch_xapian_tcpsrv(
const string & args)
114 signal(SIGCHLD, SIG_DFL);
121 if (RUNNING_ON_VALGRIND) cmd =
"./runsrv " + cmd;
124 if (socketpair(AF_UNIX, SOCK_STREAM|
SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
125 string msg(
"Couldn't create socketpair: ");
130 pid_t child = fork();
141 if (fds[1] == 1 || fds[1] == 2) {
148 execl(
"/bin/sh",
"/bin/sh",
"-c", cmd.c_str(),
static_cast<void*
>(0));
155 int fork_errno = errno;
157 string msg(
"Couldn't fork: ");
165 FILE * fh = fdopen(fds[0],
"r");
167 string msg(
"Failed to run command '");
177 if (fgets(buf,
sizeof(buf), fh) == NULL) {
181 if (waitpid(child, &status, 0) == -1) {
182 string msg(
"waitpid failed: ");
186 if (++port < 65536 && status != 0) {
187 if (WIFEXITED(status) && WEXITSTATUS(status) == 69) {
194 string msg(
"Failed to get 'Listening...' from command '");
196 msg +=
"' (output: ";
201 if (strcmp(buf,
"Listening...\n") == 0)
break;
207 int tracked_fd = dup(fds[0]);
215 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
216 if (pid_to_fd[i].pid == 0) {
217 pid_to_fd[i].fd = tracked_fd;
218 pid_to_fd[i].pid = child;
225 signal(SIGCHLD, on_SIGCHLD);
230 #elif defined __WIN32__ 232 static HANDLE tcpsrv_handles[16];
233 static unsigned tcpsrv_handles_index = 0;
235 static constexpr
auto TCPSRV_HANDLES_INDEX_MAX =
236 sizeof(tcpsrv_handles) /
sizeof(tcpsrv_handles[0]);
238 XAPIAN_NORETURN(
static void win32_throw_error_string(
const char *
str));
239 static void win32_throw_error_string(
const char *
str)
244 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
245 0, GetLastError(), 0, (CHAR*)&error, 0, 0);
248 if (len >= 2 && error[len - 2] ==
'\r' && error[len - 1] ==
'\n')
252 msg.append(error, len);
262 launch_xapian_tcpsrv(
const string & args)
273 HANDLE hRead, hWrite;
274 if (!CreatePipe(&hRead, &hWrite, 0, 0))
275 win32_throw_error_string(
"Couldn't create pipe");
278 SetHandleInformation(hWrite, HANDLE_FLAG_INHERIT, 1);
281 PROCESS_INFORMATION procinfo;
282 memset(&procinfo, 0,
sizeof(PROCESS_INFORMATION));
284 STARTUPINFO startupinfo;
285 memset(&startupinfo, 0,
sizeof(STARTUPINFO));
286 startupinfo.cb =
sizeof(STARTUPINFO);
287 startupinfo.hStdError = hWrite;
288 startupinfo.hStdOutput = hWrite;
289 startupinfo.hStdInput = INVALID_HANDLE_VALUE;
290 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
294 char * cmdline = strdup(cmd.c_str());
295 ok = CreateProcess(0, cmdline, 0, 0, TRUE, 0, 0, 0, &startupinfo, &procinfo);
298 win32_throw_error_string(
"Couldn't create child process");
301 CloseHandle(procinfo.hThread);
304 FILE *fh = fdopen(_open_osfhandle(intptr_t(hRead), O_RDONLY),
"r");
307 if (fgets(buf,
sizeof(buf), fh) == NULL) {
312 while (GetExitCodeProcess(procinfo.hProcess, &rc) && rc == STILL_ACTIVE) {
315 CloseHandle(procinfo.hProcess);
316 if (++port < 65536 && rc == 69) {
322 string msg(
"Failed to get 'Listening...' from command '");
324 msg +=
"' (output: ";
329 if (strcmp(buf,
"Listening...\r\n") == 0)
break;
334 if (tcpsrv_handles_index < TCPSRV_HANDLES_INDEX_MAX) {
335 tcpsrv_handles[tcpsrv_handles_index++] = procinfo.hProcess;
342 # error Neither HAVE_FORK nor __WIN32__ is defined 352 return "remotetcp_" + sub_manager->get_dbtype();
367 string args = get_writable_database_args(name, file);
368 int port = launch_xapian_tcpsrv(args);
376 string args = get_remote_database_args(files, timeout);
377 int port = launch_xapian_tcpsrv(args);
384 string args = get_remote_database_args(path, 300000);
385 int port = launch_xapian_tcpsrv(args);
392 string args = get_writable_database_as_database_args();
393 int port = launch_xapian_tcpsrv(args);
400 string args = get_writable_database_again_args();
401 int port = launch_xapian_tcpsrv(args);
409 signal(SIGCHLD, SIG_DFL);
410 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
411 pid_t child = pid_to_fd[i].pid;
414 while (waitpid(child, &status, 0) == -1 && errno == EINTR) { }
419 int fd = pid_to_fd[i].fd;
421 pid_to_fd[i].pid = 0;
425 #elif defined __WIN32__ 426 for (
unsigned i = 0; i != tcpsrv_handles_index; ++i) {
427 WaitForSingleObject(tcpsrv_handles[i], INFINITE);
428 CloseHandle(tcpsrv_handles[i]);
430 tcpsrv_handles_index = 0;
Xapian::WritableDatabase get_writable_database(const std::string &name, const std::string &file)
Create a RemoteTcp Xapian::WritableDatabase object indexing a single file.
~BackendManagerRemoteTcp()
Define the XAPIAN_NORETURN macro.
This class is used to access a database, or a group of databases.
Xapian::Database do_get_database(const std::vector< std::string > &files)
Create a Xapian::Database object indexing multiple files.
unsigned timeout
A timeout value in milliseconds.
std::string get_dbtype() const
Return a string representing the current database type.
include <sys/socket.h> with portability workarounds.
Convert errno value to std::string, thread-safe if possible.
Convert types to std::string.
BackendManager subclass for remotetcp databases.
Xapian::Database get_writable_database_as_database()
Create a Database object for the last opened WritableDatabase.
This class provides read/write access to a database.
void errno_to_string(int e, string &s)
Database open(const std::string &host, unsigned int port, useconds_t timeout=10000, useconds_t connect_timeout=10000)
Construct a Database object for read-only access to a remote database accessed via a TCP connection...
Public interfaces for the Xapian library.
string str(int value)
Convert int to std::string.
Xapian::Database get_remote_database(const std::vector< std::string > &files, unsigned int timeout)
Create a RemoteTcp Xapian::Database with the specified timeout.
Xapian::Database get_database_by_path(const std::string &path)
Get a RemoteTcp Xapian::Database instance of the database at path.
include <windows.h> without all the bloat and damage.
Xapian::WritableDatabase get_writable_database_again()
Create a WritableDatabase object for the last opened WritableDatabase.
WritableDatabase open_writable(const std::string &host, unsigned int port, useconds_t timeout=0, useconds_t connect_timeout=10000, int flags=0)
Construct a WritableDatabase object for update access to a remote database accessed via a TCP connect...
void clean_up()
Called after each test, to perform any necessary cleanup.
include <fcntl.h>, but working around broken platforms.