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