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();
143 if (fds[1] == 1 || fds[1] == 2) {
150 execl(
"/bin/sh",
"/bin/sh",
"-c", cmd.c_str(),
static_cast<void*
>(0));
157 int fork_errno = errno;
159 string msg(
"Couldn't fork: ");
167 FILE * fh = fdopen(fds[0],
"r");
169 string msg(
"Failed to run command '");
179 if (fgets(buf,
sizeof(buf), fh) == NULL) {
183 if (waitpid(child, &status, 0) == -1) {
184 string msg(
"waitpid failed: ");
188 if (++port < 65536 && status != 0) {
189 if (WIFEXITED(status) &&
196 string msg(
"Failed to get 'Listening...' from command '");
198 msg +=
"' (output: ";
203 if (strcmp(buf,
"Listening...\n") == 0)
break;
209 int tracked_fd = dup(fds[0]);
217 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
218 if (pid_to_fd[i].pid == 0) {
219 pid_to_fd[i].fd = tracked_fd;
220 pid_to_fd[i].pid = child;
227 signal(SIGCHLD, on_SIGCHLD);
232 #elif defined __WIN32__ 234 static HANDLE tcpsrv_handles[16];
235 static unsigned tcpsrv_handles_index = 0;
237 static constexpr
auto TCPSRV_HANDLES_INDEX_MAX =
238 sizeof(tcpsrv_handles) /
sizeof(tcpsrv_handles[0]);
240 XAPIAN_NORETURN(
static void win32_throw_error_string(
const char *
str));
241 static void win32_throw_error_string(
const char *
str)
246 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
247 0, GetLastError(), 0, (CHAR*)&error, 0, 0);
250 if (len >= 2 && error[len - 2] ==
'\r' && error[len - 1] ==
'\n')
254 msg.append(error, len);
264 launch_xapian_tcpsrv(
const string & args)
275 HANDLE hRead, hWrite;
276 if (!CreatePipe(&hRead, &hWrite, 0, 0))
277 win32_throw_error_string(
"Couldn't create pipe");
280 SetHandleInformation(hWrite, HANDLE_FLAG_INHERIT, 1);
283 PROCESS_INFORMATION procinfo;
284 memset(&procinfo, 0,
sizeof(PROCESS_INFORMATION));
286 STARTUPINFO startupinfo;
287 memset(&startupinfo, 0,
sizeof(STARTUPINFO));
288 startupinfo.cb =
sizeof(STARTUPINFO);
289 startupinfo.hStdError = hWrite;
290 startupinfo.hStdOutput = hWrite;
291 startupinfo.hStdInput = INVALID_HANDLE_VALUE;
292 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
296 if (!CreateProcess(
XAPIAN_TCPSRV, &cmd[0], 0, 0, TRUE, 0, 0, 0,
297 &startupinfo, &procinfo)) {
298 win32_throw_error_string(
"Couldn't create child process");
302 CloseHandle(procinfo.hThread);
305 FILE *fh = fdopen(_open_osfhandle(intptr_t(hRead), O_RDONLY),
"r");
308 if (fgets(buf,
sizeof(buf), fh) == NULL) {
313 while (GetExitCodeProcess(procinfo.hProcess, &rc) && rc == STILL_ACTIVE) {
316 CloseHandle(procinfo.hProcess);
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 361 string args = get_writable_database_args(name, file);
362 int port = launch_xapian_tcpsrv(args);
370 string args = get_remote_database_args(files, timeout);
371 int port = launch_xapian_tcpsrv(args);
378 string args = get_remote_database_args(path, 300000);
379 int port = launch_xapian_tcpsrv(args);
386 string args = get_writable_database_as_database_args();
387 int port = launch_xapian_tcpsrv(args);
394 string args = get_writable_database_again_args();
395 int port = launch_xapian_tcpsrv(args);
403 signal(SIGCHLD, SIG_DFL);
404 for (
unsigned i = 0; i <
sizeof(pid_to_fd) /
sizeof(pid_fd); ++i) {
405 pid_t child = pid_to_fd[i].pid;
408 while (waitpid(child, &status, 0) == -1 && errno == EINTR) { }
413 int fd = pid_to_fd[i].fd;
415 pid_to_fd[i].pid = 0;
419 #elif defined __WIN32__ 420 for (
unsigned i = 0; i != tcpsrv_handles_index; ++i) {
421 WaitForSingleObject(tcpsrv_handles[i], INFINITE);
422 CloseHandle(tcpsrv_handles[i]);
424 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.