12 # include <sys/wait.h>
23 struct Process::Impl {
27 const Arguments &args,
31 static Impl *EnvHelper(
33 const Arguments &args,
34 const Environment &env);
36 size_t WriteIn(const void *buffer, size_t max_len);
39 size_t ReadOut(void *buffer, size_t max_len);
42 size_t ReadErr(void *buffer, size_t max_len);
57 PROCESS_INFORMATION pi;
73 const Arguments &args)
74 : impl(new Impl(path, args, nullptr)) {
80 const Arguments &args,
81 const Environment &env)
82 : impl(Impl::EnvHelper(path, args, env)) {
91 size_t Process::WriteIn(const void *buffer, size_t max_len) {
92 return impl->WriteIn(buffer, max_len);
95 void Process::CloseIn() {
99 size_t Process::ReadOut(void *buffer, size_t max_len) {
100 return impl->ReadOut(buffer, max_len);
103 void Process::CloseOut() {
107 size_t Process::ReadErr(void *buffer, size_t max_len) {
108 return impl->ReadErr(buffer, max_len);
111 void Process::CloseErr() {
115 void Process::Terminate() {
119 bool Process::Terminated() {
120 return impl->Terminated();
123 int Process::Join() {
127 Process::Impl *Process::Impl::EnvHelper(
129 const Arguments &args,
130 const Environment &env
132 char *envp[env.size() + 1];
133 for (size_t i = 0; i < env.size(); ++i) {
134 envp[i] = const_cast<char *>(env[i].c_str());
136 envp[env.size()] = nullptr;
137 return new Impl(path, args, envp);
141 const string &path_in,
142 const Arguments &args,
148 , err_closed(false) {
149 const char *path = path_in.c_str();
152 for (const auto &arg : args) {
158 SECURITY_ATTRIBUTES sa;
159 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
160 sa.bInheritHandle = true;
161 sa.lpSecurityDescriptor = nullptr;
162 if (!CreatePipe(&fd_in[0], &fd_in[1], &sa, 0)) {
163 throw runtime_error("failed to open pipe for child process' stdin");
165 if (!SetHandleInformation(fd_in[1], HANDLE_FLAG_INHERIT, 0)) {
166 throw runtime_error("failed to stop child process from inheriting stdin write handle");
168 if (!CreatePipe(&fd_out[0], &fd_out[1], &sa, 0)) {
169 throw runtime_error("failed to open pipe for child process' stdout");
171 if (!SetHandleInformation(fd_out[0], HANDLE_FLAG_INHERIT, 0)) {
172 throw runtime_error("failed to stop child process from inheriting stdout read handle");
174 if (!CreatePipe(&fd_err[0], &fd_err[1], &sa, 0)) {
175 throw runtime_error("failed to open pipe for child process' stderr");
177 if (!SetHandleInformation(fd_err[0], HANDLE_FLAG_INHERIT, 0)) {
178 throw runtime_error("failed to stop child process from inheriting stderr read handle");
182 ZeroMemory(&si, sizeof(STARTUPINFO));
183 si.cb = sizeof(STARTUPINFO);
184 si.hStdError = fd_err[1];
185 si.hStdOutput = fd_out[1];
186 si.hStdInput = fd_in[0];
187 si.dwFlags |= STARTF_USESTDHANDLES;
188 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
202 throw runtime_error("CreateProcess");
205 char *argv[args.size() + 1];
206 for (size_t i = 0; i < args.size(); ++i) {
207 // I was promised exec won't modify my characters
208 argv[i] = const_cast<char *>(args[i].c_str());
210 argv[args.size()] = nullptr;
212 if (pipe(fd_in) != 0) {
213 throw SysError("failed to open pipe for child process' stdin");
215 if (pipe(fd_out) != 0) {
216 throw SysError("failed to open pipe for child process' stdout");
218 if (pipe(fd_err) != 0) {
219 throw SysError("failed to open pipe for child process' stderr");
224 throw SysError("fork");
225 } else if (pid == 0) {
227 if (dup2(fd_in[0], STDIN_FILENO) == -1) {
230 if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
233 if (dup2(fd_err[1], STDERR_FILENO) == -1) {
245 execve(path, argv, envp);
249 // if execve returns, something bad happened
262 Process::Impl::~Impl() {
268 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
271 if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
272 throw runtime_error("failed to write to child process' input stream");
276 int written = write(fd_in[1], buffer, max_len);
278 if (errno == EAGAIN) {
281 throw SysError("failed to write to child process' input stream");
288 void Process::Impl::CloseIn() {
293 CloseHandle(fd_in[1]);
300 size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
303 if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
304 throw runtime_error("failed to read from child process' output stream");
308 int ret = read(fd_out[0], buffer, max_len);
310 if (errno == EAGAIN) {
313 throw SysError("failed to read from child process' output stream");
320 void Process::Impl::CloseOut() {
325 CloseHandle(fd_out[0]);
332 size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
335 if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
336 throw runtime_error("failed to read from child process' error stream");
340 int ret = read(fd_err[0], buffer, max_len);
342 if (errno == EAGAIN) {
345 throw SysError("failed to read from child process' error stream");
352 void Process::Impl::CloseErr() {
357 CloseHandle(fd_err[0]);
364 void Process::Impl::Terminate() {
366 // can only terminate once
370 if (!TerminateProcess(pi.hProcess, -1)) {
372 if (kill(pid, SIGTERM) == -1) {
374 throw SysError("failed to terminate child process");
378 bool Process::Impl::Terminated() {
384 GetExitCodeProcess(pi.hProcess, &exit_code);
385 if (exit_code == STILL_ACTIVE) {
388 CloseHandle(pi.hProcess);
389 CloseHandle(pi.hThread);
396 int result = waitpid(pid, &stat, WNOHANG);
398 throw SysError("error polling child process");
399 } else if (result == 0) {
401 } else if (result == pid) {
402 // child just exited, reap
403 if (WIFEXITED(stat)) {
404 // autonomous termination
405 status = WEXITSTATUS(stat);
406 } else if (WIFSIGNALED(stat)) {
407 // signalled termination
408 status = WTERMSIG(stat);
413 throw runtime_error("bogus return value of waitpid");
418 int Process::Impl::Join() {
420 // can only join once
425 WaitForSingleObject(pi.hProcess, INFINITE);
426 GetExitCodeProcess(pi.hProcess, &exit_code);
427 CloseHandle(pi.hProcess);
428 CloseHandle(pi.hThread);
435 int result = waitpid(pid, &stat, 0);
437 throw SysError("error waiting on child process");
440 // should in theory only happen with WNOHANG set
443 if (WIFEXITED(stat)) {
444 // autonomous termination
445 status = WEXITSTATUS(stat);
449 if (WIFSIGNALED(stat)) {
450 // signalled termination
451 status = WTERMSIG(stat);
455 // otherwise, child probably signalled stop/continue, which we
456 // don't care about (please don't tell youth welfare), so try again