36 # include <sys/types.h> 38 # include <sys/wait.h> 41 # if !defined SIGCHLD && defined SIGCLD 42 # define SIGCHLD SIGCLD 61 # include <valgrind/memcheck.h> 69 #define LOCALHOST "127.0.0.1" 72 #define DEFAULT_PORT 1239 84 static pid_fd pid_to_fd[16];
93 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
94 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
95 if (pid_to_fd[i].pid == child) {
96 int fd = pid_to_fd[i].fd;
110 launch_xapian_tcpsrv(
const string & args)
116 signal(SIGCHLD, SIG_DFL);
123 if (RUNNING_ON_VALGRIND) cmd =
"./runsrv " + cmd;
126 if (socketpair(AF_UNIX, SOCK_STREAM|
SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
127 string msg(
"Couldn't create socketpair: ");
132 pid_t child = fork();
147 if (fds[1] == 1 || fds[1] == 2) {
154 execl(
"/bin/sh",
"/bin/sh",
"-c", cmd.c_str(),
static_cast<void*
>(0));
161 int fork_errno = errno;
163 string msg(
"Couldn't fork: ");
171 FILE * fh = fdopen(fds[0],
"r");
173 string msg(
"Failed to run command '");
183 if (fgets(buf,
sizeof(buf), fh) == NULL) {
187 if (waitpid(child, &status, 0) == -1) {
188 string msg(
"waitpid failed: ");
192 if (++port < 65536 && status != 0) {
193 if (WIFEXITED(status) &&
200 string msg(
"Failed to get 'Listening...' from command '");
202 msg +=
"' (output: ";
207 if (strcmp(buf,
"Listening...\n") == 0)
break;
213 int tracked_fd = dup(fds[0]);
221 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
222 if (pid_to_fd[i].pid == 0) {
223 pid_to_fd[i].fd = tracked_fd;
224 pid_to_fd[i].pid = child;
231 signal(SIGCHLD, on_SIGCHLD);
236 #elif defined __WIN32__ 238 static HANDLE tcpsrv_handles[16];
239 static unsigned tcpsrv_handles_index = 0;
241 static constexpr
auto TCPSRV_HANDLES_INDEX_MAX =
242 sizeof(tcpsrv_handles) /
sizeof(tcpsrv_handles[0]);
244 XAPIAN_NORETURN(
static void win32_throw_error_string(
const char *
str));
245 static void win32_throw_error_string(
const char *
str)
250 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
251 0, GetLastError(), 0, (CHAR*)&error, 0, 0);
254 if (len >= 2 && error[len - 2] ==
'\r' && error[len - 1] ==
'\n')
258 msg.append(error, len);
268 launch_xapian_tcpsrv(
const string & args)
279 HANDLE hRead, hWrite;
280 if (!CreatePipe(&hRead, &hWrite, 0, 0))
281 win32_throw_error_string(
"Couldn't create pipe");
284 SetHandleInformation(hWrite, HANDLE_FLAG_INHERIT, 1);
287 PROCESS_INFORMATION procinfo;
288 memset(&procinfo, 0,
sizeof(PROCESS_INFORMATION));
290 STARTUPINFO startupinfo;
291 memset(&startupinfo, 0,
sizeof(STARTUPINFO));
292 startupinfo.cb =
sizeof(STARTUPINFO);
293 startupinfo.hStdError = hWrite;
294 startupinfo.hStdOutput = hWrite;
295 startupinfo.hStdInput = INVALID_HANDLE_VALUE;
296 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
300 if (!CreateProcess(
XAPIAN_TCPSRV, &cmd[0], 0, 0, TRUE, 0, 0, 0,
301 &startupinfo, &procinfo)) {
302 win32_throw_error_string(
"Couldn't create child process");
306 CloseHandle(procinfo.hThread);
309 FILE *fh = fdopen(_open_osfhandle(intptr_t(hRead), O_RDONLY),
"r");
312 if (fgets(buf,
sizeof(buf), fh) == NULL) {
317 while (GetExitCodeProcess(procinfo.hProcess, &rc) && rc == STILL_ACTIVE) {
320 CloseHandle(procinfo.hProcess);
326 string msg(
"Failed to get 'Listening...' from command '");
328 msg +=
"' (output: ";
333 if (strcmp(buf,
"Listening...\r\n") == 0)
break;
338 if (tcpsrv_handles_index < TCPSRV_HANDLES_INDEX_MAX) {
339 tcpsrv_handles[tcpsrv_handles_index++] = procinfo.hProcess;
346 # error Neither HAVE_FORK nor __WIN32__ is defined 365 string args = get_writable_database_args(name, file);
366 int port = launch_xapian_tcpsrv(args);
374 string args = get_remote_database_args(files, timeout);
375 int port = launch_xapian_tcpsrv(args);
382 string args = get_remote_database_args(path, 300000);
383 int port = launch_xapian_tcpsrv(args);
390 string args = get_writable_database_as_database_args();
391 int port = launch_xapian_tcpsrv(args);
398 string args = get_writable_database_again_args();
399 int port = launch_xapian_tcpsrv(args);
407 signal(SIGCHLD, SIG_DFL);
408 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
409 pid_t child = pid_to_fd[i].pid;
412 while (waitpid(child, &status, 0) == -1 && errno == EINTR) { }
417 int fd = pid_to_fd[i].fd;
419 pid_to_fd[i].pid = 0;
423 #elif defined __WIN32__ 424 for (
unsigned i = 0; i != tcpsrv_handles_index; ++i) {
425 WaitForSingleObject(tcpsrv_handles[i], INFINITE);
426 CloseHandle(tcpsrv_handles[i]);
428 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.
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.
include <sysexits.h> with portability workarounds.
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.