12 # include <sys/select.h>
13 # include <sys/wait.h>
24 struct Process::Impl {
28 const Arguments &args,
32 static Impl *EnvHelper(
34 const Arguments &args,
35 const Environment &env);
37 size_t WriteIn(const void *buffer, size_t max_len);
40 size_t ReadOut(void *buffer, size_t max_len, int timeout);
43 size_t ReadErr(void *buffer, size_t max_len, int timeout);
58 PROCESS_INFORMATION pi;
74 const Arguments &args)
75 : impl(new Impl(path, args, nullptr)) {
81 const Arguments &args,
82 const Environment &env)
83 : impl(Impl::EnvHelper(path, args, env)) {
92 size_t Process::WriteIn(const void *buffer, size_t max_len) {
93 return impl->WriteIn(buffer, max_len);
96 void Process::CloseIn() {
100 size_t Process::ReadOut(void *buffer, size_t max_len, int timeout) {
101 return impl->ReadOut(buffer, max_len, timeout);
104 void Process::CloseOut() {
108 size_t Process::ReadErr(void *buffer, size_t max_len, int timeout) {
109 return impl->ReadErr(buffer, max_len, timeout);
112 void Process::CloseErr() {
116 void Process::Terminate() {
120 bool Process::Terminated() {
121 return impl->Terminated();
124 int Process::Join() {
128 Process::Impl *Process::Impl::EnvHelper(
130 const Arguments &args,
131 const Environment &env
133 char *envp[env.size() + 1];
134 for (size_t i = 0; i < env.size(); ++i) {
135 envp[i] = const_cast<char *>(env[i].c_str());
137 envp[env.size()] = nullptr;
138 return new Impl(path, args, envp);
142 const string &path_in,
143 const Arguments &args,
149 , err_closed(false) {
150 const char *path = path_in.c_str();
153 for (const auto &arg : args) {
159 SECURITY_ATTRIBUTES sa;
160 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
161 sa.bInheritHandle = true;
162 sa.lpSecurityDescriptor = nullptr;
163 if (!CreatePipe(&fd_in[0], &fd_in[1], &sa, 0)) {
164 throw runtime_error("failed to open pipe for child process' stdin");
166 if (!SetHandleInformation(fd_in[1], HANDLE_FLAG_INHERIT, 0)) {
167 throw runtime_error("failed to stop child process from inheriting stdin write handle");
169 if (!CreatePipe(&fd_out[0], &fd_out[1], &sa, 0)) {
170 throw runtime_error("failed to open pipe for child process' stdout");
172 if (!SetHandleInformation(fd_out[0], HANDLE_FLAG_INHERIT, 0)) {
173 throw runtime_error("failed to stop child process from inheriting stdout read handle");
175 if (!CreatePipe(&fd_err[0], &fd_err[1], &sa, 0)) {
176 throw runtime_error("failed to open pipe for child process' stderr");
178 if (!SetHandleInformation(fd_err[0], HANDLE_FLAG_INHERIT, 0)) {
179 throw runtime_error("failed to stop child process from inheriting stderr read handle");
183 ZeroMemory(&si, sizeof(STARTUPINFO));
184 si.cb = sizeof(STARTUPINFO);
185 si.hStdError = fd_err[1];
186 si.hStdOutput = fd_out[1];
187 si.hStdInput = fd_in[0];
188 si.dwFlags |= STARTF_USESTDHANDLES;
189 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
203 throw runtime_error("CreateProcess");
206 char *argv[args.size() + 1];
207 for (size_t i = 0; i < args.size(); ++i) {
208 // I was promised exec won't modify my characters
209 argv[i] = const_cast<char *>(args[i].c_str());
211 argv[args.size()] = nullptr;
213 if (pipe(fd_in) != 0) {
214 throw SysError("failed to open pipe for child process' stdin");
216 if (pipe(fd_out) != 0) {
217 throw SysError("failed to open pipe for child process' stdout");
219 if (pipe(fd_err) != 0) {
220 throw SysError("failed to open pipe for child process' stderr");
225 throw SysError("fork");
226 } else if (pid == 0) {
228 if (dup2(fd_in[0], STDIN_FILENO) == -1) {
231 if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
234 if (dup2(fd_err[1], STDERR_FILENO) == -1) {
246 execve(path, argv, envp);
250 // if execve returns, something bad happened
263 Process::Impl::~Impl() {
269 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
272 if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
273 throw runtime_error("failed to write to child process' input stream");
277 int written = write(fd_in[1], buffer, max_len);
279 if (errno == EAGAIN) {
282 throw SysError("failed to write to child process' input stream");
289 void Process::Impl::CloseIn() {
294 CloseHandle(fd_in[1]);
301 size_t Process::Impl::ReadOut(void *buffer, size_t max_len, int timeout) {
303 // TODO: timeout implementation for windows child process I/O
305 if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
306 throw runtime_error("failed to read from child process' output stream");
315 FD_SET(fd_out[0], &read_set);
316 FD_SET(fd_out[0], &error_set);
318 timer.tv_sec = timeout / 1000;
319 timer.tv_usec = (timeout % 1000) * 1000;
320 if (select(fd_out[0] + 1, &read_set, nullptr, &error_set, &timer) == -1) {
321 throw SysError("error waiting on child process' output stream");
323 if (FD_ISSET(fd_out[0], &error_set)) {
324 throw runtime_error("error condition on child process' output stream");
326 if (!FD_ISSET(fd_out[0], &read_set)) {
327 throw runtime_error("timeout while waiting on child process' output stream");
330 int ret = read(fd_out[0], buffer, max_len);
332 if (errno == EAGAIN) {
335 throw SysError("failed to read from child process' output stream");
342 void Process::Impl::CloseOut() {
347 CloseHandle(fd_out[0]);
354 size_t Process::Impl::ReadErr(void *buffer, size_t max_len, int timeout) {
356 // TODO: timeout implementation for windows child process I/O
358 if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
359 throw runtime_error("failed to read from child process' error stream");
368 FD_SET(fd_err[0], &read_set);
369 FD_SET(fd_err[0], &error_set);
371 timer.tv_sec = timeout / 1000;
372 timer.tv_usec = (timeout % 1000) * 1000;
373 if (select(fd_err[0] + 1, &read_set, nullptr, &error_set, &timer) == -1) {
374 throw SysError("error waiting on child process' error stream");
376 if (FD_ISSET(fd_err[0], &error_set)) {
377 throw runtime_error("error condition on child process' error stream");
379 if (!FD_ISSET(fd_err[0], &read_set)) {
380 throw runtime_error("timeout while waiting on child process' error stream");
383 int ret = read(fd_err[0], buffer, max_len);
385 if (errno == EAGAIN) {
388 throw SysError("failed to read from child process' error stream");
395 void Process::Impl::CloseErr() {
400 CloseHandle(fd_err[0]);
407 void Process::Impl::Terminate() {
409 // can only terminate once
413 if (!TerminateProcess(pi.hProcess, -1)) {
415 if (kill(pid, SIGTERM) == -1) {
417 throw SysError("failed to terminate child process");
421 bool Process::Impl::Terminated() {
427 GetExitCodeProcess(pi.hProcess, &exit_code);
428 if (exit_code == STILL_ACTIVE) {
431 CloseHandle(pi.hProcess);
432 CloseHandle(pi.hThread);
439 int result = waitpid(pid, &stat, WNOHANG);
441 throw SysError("error polling child process");
442 } else if (result == 0) {
444 } else if (result == pid) {
445 // child just exited, reap
446 if (WIFEXITED(stat)) {
447 // autonomous termination
448 status = WEXITSTATUS(stat);
449 } else if (WIFSIGNALED(stat)) {
450 // signalled termination
451 status = WTERMSIG(stat);
456 throw runtime_error("bogus return value of waitpid");
461 int Process::Impl::Join() {
463 // can only join once
468 WaitForSingleObject(pi.hProcess, INFINITE);
469 GetExitCodeProcess(pi.hProcess, &exit_code);
470 CloseHandle(pi.hProcess);
471 CloseHandle(pi.hThread);
478 int result = waitpid(pid, &stat, 0);
480 throw SysError("error waiting on child process");
483 // should in theory only happen with WNOHANG set
486 if (WIFEXITED(stat)) {
487 // autonomous termination
488 status = WEXITSTATUS(stat);
492 if (WIFSIGNALED(stat)) {
493 // signalled termination
494 status = WTERMSIG(stat);
498 // otherwise, child probably signalled stop/continue, which we
499 // don't care about (please don't tell youth welfare), so try again