00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "safeerrno.h"
00026 #include "safefcntl.h"
00027
00028 #include "progclient.h"
00029 #include <xapian/error.h>
00030 #include "omdebug.h"
00031
00032 #include <string>
00033 #include <vector>
00034
00035 #include <sys/types.h>
00036 #ifndef __WIN32__
00037 # include <sys/socket.h>
00038 # include <sys/wait.h>
00039 #else
00040 # include <io.h>
00041 #endif
00042
00043 using namespace std;
00044
00045 #ifndef __WIN32__
00046
00049 static void
00050 split_words(const string &text, vector<string> &words, char ws = ' ')
00051 {
00052 size_t i = 0;
00053 if (i < text.length() && text[0] == ws) {
00054 i = text.find_first_not_of(ws, i);
00055 }
00056 while (i < text.length()) {
00057 size_t j = text.find_first_of(ws, i);
00058 words.push_back(text.substr(i, j - i));
00059 i = text.find_first_not_of(ws, j);
00060 }
00061 }
00062 #endif
00063
00064 ProgClient::ProgClient(const string &progname, const string &args,
00065 int msecs_timeout, bool writable)
00066 : RemoteDatabase(run_program(progname, args
00067 #ifndef __WIN32__
00068 , pid
00069 #endif
00070 ),
00071 msecs_timeout,
00072 get_progcontext(progname, args),
00073 writable)
00074 {
00075 DEBUGCALL(DB, void, "ProgClient::ProgClient", progname << ", " << args <<
00076 ", " << msecs_timeout << ", " << writable);
00077 }
00078
00079 string
00080 ProgClient::get_progcontext(const string &progname, const string &args)
00081 {
00082 DEBUGCALL_STATIC(DB, string, "ProgClient::get_progcontext", progname <<
00083 ", " << args);
00084 RETURN("remote:prog(" + progname + " " + args);
00085 }
00086
00087 int
00088 ProgClient::run_program(const string &progname, const string &args
00089 #ifndef __WIN32__
00090 , pid_t &pid
00091 #endif
00092 )
00093 {
00094 #if defined HAVE_SOCKETPAIR && defined HAVE_FORK
00095 DEBUGCALL_STATIC(DB, int, "ProgClient::run_program", progname << ", " <<
00096 args << ", [&pid]");
00097
00098
00099
00100 int sv[2];
00101
00102 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) {
00103 throw Xapian::NetworkError(string("socketpair failed"), get_progcontext(progname, args), errno);
00104 }
00105
00106 pid = fork();
00107
00108 if (pid < 0) {
00109 throw Xapian::NetworkError(string("fork failed"), get_progcontext(progname, args), errno);
00110 }
00111
00112 if (pid != 0) {
00113
00114
00115 close(sv[1]);
00116 return sv[0];
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 close(0);
00126 close(1);
00127 dup2(sv[1], 0);
00128 dup2(sv[1], 1);
00129
00130
00131
00132 for (int fd = 2; fd < 256; ++fd) {
00133 close(fd);
00134 }
00135
00136
00137 int stderrfd = open("/dev/null", O_WRONLY);
00138 if (stderrfd == -1) {
00139 throw Xapian::NetworkError(string("Redirecting stderr to /dev/null failed"), get_progcontext(progname, args), errno);
00140 }
00141 if (stderrfd != 2) {
00142
00143 dup2(stderrfd, 2);
00144 close(stderrfd);
00145 }
00146
00147 vector<string> argvec;
00148 split_words(args, argvec);
00149
00150
00151
00152 const char **new_argv = new const char *[argvec.size() + 2];
00153
00154 new_argv[0] = progname.c_str();
00155 for (vector<string>::size_type i = 0; i < argvec.size(); ++i) {
00156 new_argv[i + 1] = argvec[i].c_str();
00157 }
00158 new_argv[argvec.size() + 1] = 0;
00159 execvp(progname.c_str(), const_cast<char *const *>(new_argv));
00160
00161
00162
00163
00164 _exit(-1);
00165 #ifdef __sgi
00166
00167 return 0;
00168 #endif
00169 #elif defined __WIN32__
00170 DEBUGCALL_STATIC(DB, int, "ProgClient::run_program", progname << ", " <<
00171 args);
00172
00173 static unsigned int pipecount = 0;
00174 char pipename[256];
00175 sprintf(pipename, "\\\\.\\pipe\\xapian-remote-%lx-%lx-%x",
00176 (unsigned long)GetCurrentProcessId(),
00177 (unsigned long)GetCurrentThreadId(), pipecount++);
00178
00179 HANDLE hPipe = CreateNamedPipe(pipename,
00180 PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
00181 0,
00182 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT,
00183 NULL);
00184
00185 if (hPipe == INVALID_HANDLE_VALUE) {
00186 throw Xapian::NetworkError("CreateNamedPipe failed",
00187 get_progcontext(progname, args),
00188 -(int)GetLastError());
00189 }
00190
00191 HANDLE hClient = CreateFile(pipename,
00192 GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
00193 FILE_FLAG_OVERLAPPED, NULL);
00194
00195 if (hClient == INVALID_HANDLE_VALUE) {
00196 throw Xapian::NetworkError("CreateFile failed",
00197 get_progcontext(progname, args),
00198 -(int)GetLastError());
00199 }
00200
00201 if (!ConnectNamedPipe(hPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED) {
00202 throw Xapian::NetworkError("ConnectNamedPipe failed",
00203 get_progcontext(progname, args),
00204 -(int)GetLastError());
00205 }
00206
00207
00208 SetHandleInformation(hClient, HANDLE_FLAG_INHERIT, 1);
00209
00210
00211 PROCESS_INFORMATION procinfo;
00212 memset(&procinfo, 0, sizeof(PROCESS_INFORMATION));
00213
00214 STARTUPINFO startupinfo;
00215 memset(&startupinfo, 0, sizeof(STARTUPINFO));
00216 startupinfo.cb = sizeof(STARTUPINFO);
00217 startupinfo.hStdError = hClient;
00218 startupinfo.hStdOutput = hClient;
00219 startupinfo.hStdInput = hClient;
00220 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
00221
00222
00223 BOOL ok;
00224 char * cmdline = strdup((progname + ' ' + args).c_str());
00225 ok = CreateProcess(0, cmdline, 0, 0, TRUE, 0, 0, 0, &startupinfo, &procinfo);
00226 free(cmdline);
00227 if (!ok) {
00228 throw Xapian::NetworkError("CreateProcess failed",
00229 get_progcontext(progname, args),
00230 -(int)GetLastError());
00231 }
00232
00233 CloseHandle(hClient);
00234 CloseHandle(procinfo.hThread);
00235 return _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
00236 #endif
00237 }
00238
00239 ProgClient::~ProgClient()
00240 {
00241
00242 do_close();
00243 #ifndef __WIN32__
00244 waitpid(pid, 0, 0);
00245 #endif
00246 }