| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | #include <config.h> |
|---|
| 24 | |
|---|
| 25 | #include "safeerrno.h" |
|---|
| 26 | #include "safefcntl.h" |
|---|
| 27 | |
|---|
| 28 | #include "progclient.h" |
|---|
| 29 | #include <xapian/error.h> |
|---|
| 30 | #include "omdebug.h" |
|---|
| 31 | |
|---|
| 32 | #include <string> |
|---|
| 33 | #include <vector> |
|---|
| 34 | |
|---|
| 35 | #include <sys/types.h> |
|---|
| 36 | #ifndef __WIN32__ |
|---|
| 37 | # include <sys/socket.h> |
|---|
| 38 | # include <sys/wait.h> |
|---|
| 39 | #else |
|---|
| 40 | # include <io.h> |
|---|
| 41 | #endif |
|---|
| 42 | |
|---|
| 43 | using namespace std; |
|---|
| 44 | |
|---|
| 45 | #ifndef __WIN32__ |
|---|
| 46 | |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | static void |
|---|
| 50 | split_words(const string &text, vector<string> &words, char ws = ' ') |
|---|
| 51 | { |
|---|
| 52 | size_t i = 0; |
|---|
| 53 | if (i < text.length() && text[0] == ws) { |
|---|
| 54 | i = text.find_first_not_of(ws, i); |
|---|
| 55 | } |
|---|
| 56 | while (i < text.length()) { |
|---|
| 57 | size_t j = text.find_first_of(ws, i); |
|---|
| 58 | words.push_back(text.substr(i, j - i)); |
|---|
| 59 | i = text.find_first_not_of(ws, j); |
|---|
| 60 | } |
|---|
| 61 | } |
|---|
| 62 | #endif |
|---|
| 63 | |
|---|
| 64 | ProgClient::ProgClient(const string &progname, const string &args, |
|---|
| 65 | int msecs_timeout, bool writable) |
|---|
| 66 | : RemoteDatabase(run_program(progname, args |
|---|
| 67 | #ifndef __WIN32__ |
|---|
| 68 | , pid |
|---|
| 69 | #endif |
|---|
| 70 | ), |
|---|
| 71 | msecs_timeout, |
|---|
| 72 | get_progcontext(progname, args), |
|---|
| 73 | writable) |
|---|
| 74 | { |
|---|
| 75 | DEBUGCALL(DB, void, "ProgClient::ProgClient", progname << ", " << args << |
|---|
| 76 | ", " << msecs_timeout << ", " << writable); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | string |
|---|
| 80 | ProgClient::get_progcontext(const string &progname, const string &args) |
|---|
| 81 | { |
|---|
| 82 | DEBUGCALL_STATIC(DB, string, "ProgClient::get_progcontext", progname << |
|---|
| 83 | ", " << args); |
|---|
| 84 | RETURN("remote:prog(" + progname + " " + args); |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | int |
|---|
| 88 | ProgClient::run_program(const string &progname, const string &args |
|---|
| 89 | #ifndef __WIN32__ |
|---|
| 90 | , pid_t &pid |
|---|
| 91 | #endif |
|---|
| 92 | ) |
|---|
| 93 | { |
|---|
| 94 | #if defined HAVE_SOCKETPAIR && defined HAVE_FORK |
|---|
| 95 | DEBUGCALL_STATIC(DB, int, "ProgClient::run_program", progname << ", " << |
|---|
| 96 | args << ", [&pid]"); |
|---|
| 97 | |
|---|
| 98 | |
|---|
| 99 | |
|---|
| 100 | int sv[2]; |
|---|
| 101 | |
|---|
| 102 | if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { |
|---|
| 103 | throw Xapian::NetworkError(string("socketpair failed"), get_progcontext(progname, args), errno); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | pid = fork(); |
|---|
| 107 | |
|---|
| 108 | if (pid < 0) { |
|---|
| 109 | throw Xapian::NetworkError(string("fork failed"), get_progcontext(progname, args), errno); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | if (pid != 0) { |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | close(sv[1]); |
|---|
| 116 | return sv[0]; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | |
|---|
| 120 | |
|---|
| 121 | |
|---|
| 122 | |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | close(0); |
|---|
| 126 | close(1); |
|---|
| 127 | dup2(sv[1], 0); |
|---|
| 128 | dup2(sv[1], 1); |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | for (int fd = 2; fd < 256; ++fd) { |
|---|
| 133 | close(fd); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | |
|---|
| 137 | int stderrfd = open("/dev/null", O_WRONLY); |
|---|
| 138 | if (stderrfd == -1) { |
|---|
| 139 | throw Xapian::NetworkError(string("Redirecting stderr to /dev/null failed"), get_progcontext(progname, args), errno); |
|---|
| 140 | } |
|---|
| 141 | if (stderrfd != 2) { |
|---|
| 142 | |
|---|
| 143 | dup2(stderrfd, 2); |
|---|
| 144 | close(stderrfd); |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | vector<string> argvec; |
|---|
| 148 | split_words(args, argvec); |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | const char **new_argv = new const char *[argvec.size() + 2]; |
|---|
| 153 | |
|---|
| 154 | new_argv[0] = progname.c_str(); |
|---|
| 155 | for (vector<string>::size_type i = 0; i < argvec.size(); ++i) { |
|---|
| 156 | new_argv[i + 1] = argvec[i].c_str(); |
|---|
| 157 | } |
|---|
| 158 | new_argv[argvec.size() + 1] = 0; |
|---|
| 159 | execvp(progname.c_str(), const_cast<char *const *>(new_argv)); |
|---|
| 160 | |
|---|
| 161 | |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | _exit(-1); |
|---|
| 165 | #ifdef __sgi |
|---|
| 166 | |
|---|
| 167 | return 0; |
|---|
| 168 | #endif |
|---|
| 169 | #elif defined __WIN32__ |
|---|
| 170 | DEBUGCALL_STATIC(DB, int, "ProgClient::run_program", progname << ", " << |
|---|
| 171 | args); |
|---|
| 172 | |
|---|
| 173 | static unsigned int pipecount = 0; |
|---|
| 174 | char pipename[256]; |
|---|
| 175 | sprintf(pipename, "\\\\.\\pipe\\xapian-remote-%lx-%lx-%x", |
|---|
| 176 | (unsigned long)GetCurrentProcessId(), |
|---|
| 177 | (unsigned long)GetCurrentThreadId(), pipecount++); |
|---|
| 178 | |
|---|
| 179 | HANDLE hPipe = CreateNamedPipe(pipename, |
|---|
| 180 | PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, |
|---|
| 181 | 0, |
|---|
| 182 | 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, |
|---|
| 183 | NULL); |
|---|
| 184 | |
|---|
| 185 | if (hPipe == INVALID_HANDLE_VALUE) { |
|---|
| 186 | throw Xapian::NetworkError("CreateNamedPipe failed", |
|---|
| 187 | get_progcontext(progname, args), |
|---|
| 188 | -(int)GetLastError()); |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | HANDLE hClient = CreateFile(pipename, |
|---|
| 192 | GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, |
|---|
| 193 | FILE_FLAG_OVERLAPPED, NULL); |
|---|
| 194 | |
|---|
| 195 | if (hClient == INVALID_HANDLE_VALUE) { |
|---|
| 196 | throw Xapian::NetworkError("CreateFile failed", |
|---|
| 197 | get_progcontext(progname, args), |
|---|
| 198 | -(int)GetLastError()); |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | if (!ConnectNamedPipe(hPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED) { |
|---|
| 202 | throw Xapian::NetworkError("ConnectNamedPipe failed", |
|---|
| 203 | get_progcontext(progname, args), |
|---|
| 204 | -(int)GetLastError()); |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | |
|---|
| 208 | SetHandleInformation(hClient, HANDLE_FLAG_INHERIT, 1); |
|---|
| 209 | |
|---|
| 210 | |
|---|
| 211 | PROCESS_INFORMATION procinfo; |
|---|
| 212 | memset(&procinfo, 0, sizeof(PROCESS_INFORMATION)); |
|---|
| 213 | |
|---|
| 214 | STARTUPINFO startupinfo; |
|---|
| 215 | memset(&startupinfo, 0, sizeof(STARTUPINFO)); |
|---|
| 216 | startupinfo.cb = sizeof(STARTUPINFO); |
|---|
| 217 | startupinfo.hStdError = hClient; |
|---|
| 218 | startupinfo.hStdOutput = hClient; |
|---|
| 219 | startupinfo.hStdInput = hClient; |
|---|
| 220 | startupinfo.dwFlags |= STARTF_USESTDHANDLES; |
|---|
| 221 | |
|---|
| 222 | |
|---|
| 223 | BOOL ok; |
|---|
| 224 | char * cmdline = strdup((progname + ' ' + args).c_str()); |
|---|
| 225 | ok = CreateProcess(0, cmdline, 0, 0, TRUE, 0, 0, 0, &startupinfo, &procinfo); |
|---|
| 226 | free(cmdline); |
|---|
| 227 | if (!ok) { |
|---|
| 228 | throw Xapian::NetworkError("CreateProcess failed", |
|---|
| 229 | get_progcontext(progname, args), |
|---|
| 230 | -(int)GetLastError()); |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | CloseHandle(hClient); |
|---|
| 234 | CloseHandle(procinfo.hThread); |
|---|
| 235 | return _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY); |
|---|
| 236 | #endif |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | ProgClient::~ProgClient() |
|---|
| 240 | { |
|---|
| 241 | |
|---|
| 242 | do_close(); |
|---|
| 243 | #ifndef __WIN32__ |
|---|
| 244 | waitpid(pid, 0, 0); |
|---|
| 245 | #endif |
|---|
| 246 | } |
|---|