00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023
00024 #include "backendmanager_remotetcp.h"
00025
00026 #include <xapian.h>
00027
00028 #include "safeerrno.h"
00029 #include <stdio.h>
00030 #include <cstring>
00031
00032 #ifdef HAVE_FORK
00033 # include <signal.h>
00034 # include <sys/types.h>
00035 # include <sys/socket.h>
00036 # include <sys/wait.h>
00037 # include <unistd.h>
00038
00039 # if !defined SIGCHLD && defined SIGCLD
00040 # define SIGCHLD SIGCLD
00041 # endif
00042 #endif
00043
00044 #ifdef __WIN32__
00045 # include <io.h>
00046 # include "safefcntl.h"
00047 # include "safewindows.h"
00048 #endif
00049
00050 #include "noreturn.h"
00051 #include "str.h"
00052
00053 #include <string>
00054 #include <vector>
00055
00056 #ifdef HAVE_VALGRIND
00057 # include <valgrind/memcheck.h>
00058 #endif
00059
00060 using namespace std;
00061
00062
00063
00064
00065 #define LOCALHOST "127.0.0.1"
00066
00067
00068 #define DEFAULT_PORT 1239
00069
00070 #ifdef HAVE_FORK
00071
00072
00073
00074
00075 struct pid_fd {
00076 pid_t pid;
00077 int fd;
00078 };
00079
00080 static pid_fd pid_to_fd[16];
00081
00082 extern "C" {
00083
00084 static void
00085 on_SIGCHLD(int )
00086 {
00087 int status;
00088 pid_t child;
00089 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
00090 for (unsigned i = 0; i < sizeof(pid_to_fd) / sizeof(pid_fd); ++i) {
00091 if (pid_to_fd[i].pid == child) {
00092 int fd = pid_to_fd[i].fd;
00093 pid_to_fd[i].fd = 0;
00094 pid_to_fd[i].pid = 0;
00095
00096 close(fd);
00097 break;
00098 }
00099 }
00100 }
00101 }
00102
00103 }
00104
00105 static int
00106 launch_xapian_tcpsrv(const string & args)
00107 {
00108 int port = DEFAULT_PORT;
00109
00110
00111
00112 signal(SIGCHLD, SIG_DFL);
00113 try_next_port:
00114 string cmd = XAPIAN_TCPSRV" --one-shot --interface "LOCALHOST" --port " + str(port) + " " + args;
00115 #ifdef HAVE_VALGRIND
00116 if (RUNNING_ON_VALGRIND) cmd = "./runsrv " + cmd;
00117 #endif
00118 int fds[2];
00119 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) < 0) {
00120 string msg("Couldn't create socketpair: ");
00121 msg += strerror(errno);
00122 throw msg;
00123 }
00124
00125 pid_t child = fork();
00126 if (child == 0) {
00127
00128 close(fds[0]);
00129
00130 dup2(fds[1], 1);
00131 dup2(fds[1], 2);
00132 execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), (void*)NULL);
00133 _exit(-1);
00134 }
00135
00136 close(fds[1]);
00137 if (child == -1) {
00138
00139 int fork_errno = errno;
00140 close(fds[0]);
00141 string msg("Couldn't fork: ");
00142 msg += strerror(fork_errno);
00143 throw msg;
00144 }
00145
00146
00147
00148
00149 FILE * fh = fdopen(fds[0], "r");
00150 if (fh == NULL) {
00151 string msg("Failed to run command '");
00152 msg += cmd;
00153 msg += "': ";
00154 msg += strerror(errno);
00155 throw msg;
00156 }
00157
00158 string output;
00159 while (true) {
00160 char buf[256];
00161 if (fgets(buf, sizeof(buf), fh) == NULL) {
00162 fclose(fh);
00163
00164 int status;
00165 if (waitpid(child, &status, 0) == -1) {
00166 string msg("waitpid failed: ");
00167 msg += strerror(errno);
00168 throw msg;
00169 }
00170 if (++port < 65536 && status != 0) {
00171 if (WIFEXITED(status) && WEXITSTATUS(status) == 69) {
00172
00173
00174
00175 goto try_next_port;
00176 }
00177 }
00178 string msg("Failed to get 'Listening...' from command '");
00179 msg += cmd;
00180 msg += "' (output: ";
00181 msg += output;
00182 msg += ")";
00183 throw msg;
00184 }
00185 if (strcmp(buf, "Listening...\n") == 0) break;
00186 output += buf;
00187 }
00188
00189
00190
00191 int tracked_fd = dup(fds[0]);
00192
00193
00194
00195 fclose(fh);
00196
00197
00198
00199 for (unsigned i = 0; i < sizeof(pid_to_fd) / sizeof(pid_fd); ++i) {
00200 if (pid_to_fd[i].pid == 0) {
00201 pid_to_fd[i].fd = tracked_fd;
00202 pid_to_fd[i].pid = child;
00203 break;
00204 }
00205 }
00206
00207
00208
00209 signal(SIGCHLD, on_SIGCHLD);
00210
00211 return port;
00212 }
00213
00214 #elif defined __WIN32__
00215
00216 XAPIAN_NORETURN(static void win32_throw_error_string(const char * str));
00217 static void win32_throw_error_string(const char * str)
00218 {
00219 string msg(str);
00220 char * error = 0;
00221 DWORD len;
00222 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER,
00223 0, GetLastError(), 0, (CHAR*)&error, 0, 0);
00224 if (error) {
00225
00226 if (len >= 2 && error[len - 2] == '\r' && error[len - 1] == '\n')
00227 len -= 2;
00228 if (len) {
00229 msg += ": ";
00230 msg.append(error, len);
00231 }
00232 LocalFree(error);
00233 }
00234 throw msg;
00235 }
00236
00237
00238
00239 static int
00240 launch_xapian_tcpsrv(const string & args)
00241 {
00242 int port = DEFAULT_PORT;
00243
00244 try_next_port:
00245 string cmd = XAPIAN_TCPSRV" --one-shot --interface "LOCALHOST" --port " + str(port) + " " + args;
00246
00247
00248 HANDLE hRead, hWrite;
00249 if (!CreatePipe(&hRead, &hWrite, 0, 0))
00250 win32_throw_error_string("Couldn't create pipe");
00251
00252
00253 SetHandleInformation(hWrite, HANDLE_FLAG_INHERIT, 1);
00254
00255
00256 PROCESS_INFORMATION procinfo;
00257 memset(&procinfo, 0, sizeof(PROCESS_INFORMATION));
00258
00259 STARTUPINFO startupinfo;
00260 memset(&startupinfo, 0, sizeof(STARTUPINFO));
00261 startupinfo.cb = sizeof(STARTUPINFO);
00262 startupinfo.hStdError = hWrite;
00263 startupinfo.hStdOutput = hWrite;
00264 startupinfo.hStdInput = INVALID_HANDLE_VALUE;
00265 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
00266
00267
00268 BOOL ok;
00269 char * cmdline = strdup(cmd.c_str());
00270 ok = CreateProcess(0, cmdline, 0, 0, TRUE, 0, 0, 0, &startupinfo, &procinfo);
00271 free(cmdline);
00272 if (!ok)
00273 win32_throw_error_string("Couldn't create child process");
00274
00275 CloseHandle(hWrite);
00276 CloseHandle(procinfo.hThread);
00277
00278 string output;
00279 FILE *fh = fdopen(_open_osfhandle((intptr_t)hRead, O_RDONLY), "r");
00280 while (true) {
00281 char buf[256];
00282 if (fgets(buf, sizeof(buf), fh) == NULL) {
00283 fclose(fh);
00284 DWORD rc;
00285
00286
00287 while (GetExitCodeProcess(procinfo.hProcess, &rc) && rc == STILL_ACTIVE) {
00288 Sleep(100);
00289 }
00290 CloseHandle(procinfo.hProcess);
00291 if (++port < 65536 && rc == 69) {
00292
00293
00294
00295 goto try_next_port;
00296 }
00297 string msg("Failed to get 'Listening...' from command '");
00298 msg += cmd;
00299 msg += "' (output: ";
00300 msg += output;
00301 msg += ")";
00302 throw msg;
00303 }
00304 if (strcmp(buf, "Listening...\r\n") == 0) break;
00305 output += buf;
00306 }
00307 fclose(fh);
00308
00309 return port;
00310 }
00311
00312 #else
00313 # error Neither HAVE_FORK nor __WIN32__ is defined
00314 #endif
00315
00316 BackendManagerRemoteTcp::~BackendManagerRemoteTcp() {
00317 BackendManagerRemoteTcp::clean_up();
00318 }
00319
00320 std::string
00321 BackendManagerRemoteTcp::get_dbtype() const
00322 {
00323 return "remotetcp_" + remote_type;
00324 }
00325
00326 Xapian::Database
00327 BackendManagerRemoteTcp::do_get_database(const vector<string> & files)
00328 {
00329
00330
00331 return BackendManagerRemoteTcp::get_remote_database(files, 300000);
00332 }
00333
00334 Xapian::WritableDatabase
00335 BackendManagerRemoteTcp::get_writable_database(const string & name,
00336 const string & file)
00337 {
00338 string args = get_writable_database_args(name, file);
00339 int port = launch_xapian_tcpsrv(args);
00340 return Xapian::Remote::open_writable(LOCALHOST, port);
00341 }
00342
00343 Xapian::Database
00344 BackendManagerRemoteTcp::get_remote_database(const vector<string> & files,
00345 unsigned int timeout)
00346 {
00347 string args = get_remote_database_args(files, timeout);
00348 int port = launch_xapian_tcpsrv(args);
00349 return Xapian::Remote::open(LOCALHOST, port);
00350 }
00351
00352 Xapian::Database
00353 BackendManagerRemoteTcp::get_writable_database_as_database()
00354 {
00355 string args = get_writable_database_as_database_args();
00356 int port = launch_xapian_tcpsrv(args);
00357 return Xapian::Remote::open(LOCALHOST, port);
00358 }
00359
00360 Xapian::WritableDatabase
00361 BackendManagerRemoteTcp::get_writable_database_again()
00362 {
00363 string args = get_writable_database_again_args();
00364 int port = launch_xapian_tcpsrv(args);
00365 return Xapian::Remote::open_writable(LOCALHOST, port);
00366 }
00367
00368 void
00369 BackendManagerRemoteTcp::clean_up()
00370 {
00371 #ifdef HAVE_FORK
00372 signal(SIGCHLD, SIG_DFL);
00373 for (unsigned i = 0; i < sizeof(pid_to_fd) / sizeof(pid_fd); ++i) {
00374 pid_t child = pid_to_fd[i].pid;
00375 if (child) {
00376 int status;
00377 while (waitpid(child, &status, 0) == -1 && errno == EINTR) { }
00378
00379
00380
00381
00382 int fd = pid_to_fd[i].fd;
00383 pid_to_fd[i].fd = 0;
00384 pid_to_fd[i].pid = 0;
00385 close(fd);
00386 }
00387 }
00388 #endif
00389 }