12 # include <sys/wait.h>
23 struct Process::Impl {
26 const string &path_in,
27 const Arguments &args,
28 const Environment &env);
31 size_t WriteIn(const void *buffer, size_t max_len);
34 size_t ReadOut(void *buffer, size_t max_len);
37 size_t ReadErr(void *buffer, size_t max_len);
52 PROCESS_INFORMATION pi;
68 const Arguments &args,
69 const Environment &env)
70 : impl(new Impl(path, args, env)) {
79 size_t Process::WriteIn(const void *buffer, size_t max_len) {
80 return impl->WriteIn(buffer, max_len);
83 void Process::CloseIn() {
87 size_t Process::ReadOut(void *buffer, size_t max_len) {
88 return impl->ReadOut(buffer, max_len);
91 void Process::CloseOut() {
95 size_t Process::ReadErr(void *buffer, size_t max_len) {
96 return impl->ReadErr(buffer, max_len);
99 void Process::CloseErr() {
103 void Process::Terminate() {
107 bool Process::Terminated() {
108 return impl->Terminated();
111 int Process::Join() {
117 const string &path_in,
118 const Arguments &args,
119 const Environment &env)
124 , err_closed(false) {
125 const char *path = path_in.c_str();
126 char *envp[env.size() + 1];
127 for (size_t i = 0; i < env.size(); ++i) {
128 envp[i] = const_cast<char *>(env[i].c_str());
130 envp[env.size()] = nullptr;
133 for (const auto &arg : args) {
139 SECURITY_ATTRIBUTES sa;
140 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
141 sa.bInheritHandle = true;
142 sa.lpSecurityDescriptor = nullptr;
143 if (!CreatePipe(&fd_in[0], &fd_in[1], &sa, 0)) {
144 throw runtime_error("failed to open pipe for child process' stdin");
146 if (!SetHandleInformation(fd_in[1], HANDLE_FLAG_INHERIT, 0)) {
147 throw runtime_error("failed to stop child process from inheriting stdin write handle");
149 if (!CreatePipe(&fd_out[0], &fd_out[1], &sa, 0)) {
150 throw runtime_error("failed to open pipe for child process' stdout");
152 if (!SetHandleInformation(fd_out[0], HANDLE_FLAG_INHERIT, 0)) {
153 throw runtime_error("failed to stop child process from inheriting stdout read handle");
155 if (!CreatePipe(&fd_err[0], &fd_err[1], &sa, 0)) {
156 throw runtime_error("failed to open pipe for child process' stderr");
158 if (!SetHandleInformation(fd_err[0], HANDLE_FLAG_INHERIT, 0)) {
159 throw runtime_error("failed to stop child process from inheriting stderr read handle");
163 ZeroMemory(&si, sizeof(STARTUPINFO));
164 si.cb = sizeof(STARTUPINFO);
165 si.hStdError = fd_err[1];
166 si.hStdOutput = fd_out[1];
167 si.hStdInput = fd_in[0];
168 si.dwFlags |= STARTF_USESTDHANDLES;
169 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
183 throw runtime_error("CreateProcess");
186 char *argv[args.size() + 1];
187 for (size_t i = 0; i < args.size(); ++i) {
188 // I was promised exec won't modify my characters
189 argv[i] = const_cast<char *>(args[i].c_str());
191 argv[args.size()] = nullptr;
193 if (pipe(fd_in) != 0) {
194 throw SysError("failed to open pipe for child process' stdin");
196 if (pipe(fd_out) != 0) {
197 throw SysError("failed to open pipe for child process' stdout");
199 if (pipe(fd_err) != 0) {
200 throw SysError("failed to open pipe for child process' stderr");
205 throw SysError("fork");
206 } else if (pid == 0) {
208 if (dup2(fd_in[0], STDIN_FILENO) == -1) {
211 if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
214 if (dup2(fd_err[1], STDERR_FILENO) == -1) {
225 execve(path, argv, envp);
226 // if execve returns, something bad happened
239 Process::Impl::~Impl() {
245 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
248 if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
249 throw runtime_error("failed to write to child process' input stream");
253 int written = write(fd_in[1], buffer, max_len);
255 if (errno == EAGAIN) {
258 throw SysError("failed to write to child process' input stream");
265 void Process::Impl::CloseIn() {
270 CloseHandle(fd_in[1]);
277 size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
280 if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
281 throw runtime_error("failed to read from child process' output stream");
285 int ret = read(fd_out[0], buffer, max_len);
287 if (errno == EAGAIN) {
290 throw SysError("failed to read from child process' output stream");
297 void Process::Impl::CloseOut() {
302 CloseHandle(fd_out[0]);
309 size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
312 if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
313 throw runtime_error("failed to read from child process' error stream");
317 int ret = read(fd_err[0], buffer, max_len);
319 if (errno == EAGAIN) {
322 throw SysError("failed to read from child process' error stream");
329 void Process::Impl::CloseErr() {
334 CloseHandle(fd_err[0]);
341 void Process::Impl::Terminate() {
343 // can only terminate once
347 if (!TerminateProcess(pi.hProcess, -1)) {
349 if (kill(pid, SIGTERM) == -1) {
351 throw SysError("failed to terminate child process");
355 bool Process::Impl::Terminated() {
361 GetExitCodeProcess(pi.hProcess, &exit_code);
362 if (exit_code == STILL_ACTIVE) {
365 CloseHandle(pi.hProcess);
366 CloseHandle(pi.hThread);
373 int result = waitpid(pid, &stat, WNOHANG);
375 throw SysError("error polling child process");
376 } else if (result == 0) {
378 } else if (result == pid) {
379 // child just exited, reap
380 if (WIFEXITED(stat)) {
381 // autonomous termination
382 status = WEXITSTATUS(stat);
383 } else if (WIFSIGNALED(stat)) {
384 // signalled termination
385 status = WTERMSIG(stat);
390 throw runtime_error("bogus return value of waitpid");
395 int Process::Impl::Join() {
397 // can only join once
402 WaitForSingleObject(pi.hProcess, INFINITE);
403 GetExitCodeProcess(pi.hProcess, &exit_code);
404 CloseHandle(pi.hProcess);
405 CloseHandle(pi.hThread);
412 int result = waitpid(pid, &stat, 0);
414 throw SysError("error waiting on child process");
417 // should in theory only happen with WNOHANG set
420 if (WIFEXITED(stat)) {
421 // autonomous termination
422 status = WEXITSTATUS(stat);
426 if (WIFSIGNALED(stat)) {
427 // signalled termination
428 status = WTERMSIG(stat);
432 // otherwise, child probably signalled stop/continue, which we
433 // don't care about (please don't tell youth welfare), so try again