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);
32 size_t ReadOut(void *buffer, size_t max_len);
33 size_t ReadErr(void *buffer, size_t max_len);
39 PROCESS_INFORMATION pi;
55 const Arguments &args,
56 const Environment &env)
57 : impl(new Impl(path, args, env))
68 size_t Process::WriteIn(const void *buffer, size_t max_len) {
69 return impl->WriteIn(buffer, max_len);
72 size_t Process::ReadOut(void *buffer, size_t max_len) {
73 return impl->ReadOut(buffer, max_len);
76 size_t Process::ReadErr(void *buffer, size_t max_len) {
77 return impl->ReadErr(buffer, max_len);
80 void Process::Terminate() {
90 status = impl->Join();
98 const string &path_in,
99 const Arguments &args,
100 const Environment &env
102 const char *path = path_in.c_str();
103 char *envp[env.size() + 1];
104 for (size_t i = 0; i < env.size(); ++i) {
105 envp[i] = const_cast<char *>(env[i].c_str());
107 envp[env.size()] = nullptr;
110 for (const auto &arg : args) {
116 SECURITY_ATTRIBUTES sa;
117 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
118 sa.bInheritHandle = true;
119 sa.lpSecurityDescriptor = nullptr;
120 if (!CreatePipe(&fd_in[0], &fd_in[1], &sa, 0)) {
121 throw runtime_error("failed to open pipe for child process' stdin");
123 if (!SetHandleInformation(fd_in[1], HANDLE_FLAG_INHERIT, 0)) {
124 throw runtime_error("failed to stop child process from inheriting stdin write handle");
126 if (!CreatePipe(&fd_out[0], &fd_out[1], &sa, 0)) {
127 throw runtime_error("failed to open pipe for child process' stdout");
129 if (!SetHandleInformation(fd_out[0], HANDLE_FLAG_INHERIT, 0)) {
130 throw runtime_error("failed to stop child process from inheriting stdout read handle");
132 if (!CreatePipe(&fd_err[0], &fd_err[1], &sa, 0)) {
133 throw runtime_error("failed to open pipe for child process' stderr");
135 if (!SetHandleInformation(fd_err[0], HANDLE_FLAG_INHERIT, 0)) {
136 throw runtime_error("failed to stop child process from inheriting stderr read handle");
140 ZeroMemory(&si, sizeof(STARTUPINFO));
141 si.cb = sizeof(STARTUPINFO);
142 si.hStdError = fd_err[1];
143 si.hStdOutput = fd_out[1];
144 si.hStdInput = fd_in[0];
145 si.dwFlags |= STARTF_USESTDHANDLES;
146 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
160 throw runtime_error("CreateProcess");
163 char *argv[args.size() + 1];
164 for (size_t i = 0; i < args.size(); ++i) {
165 // I was promised exec won't modify my characters
166 argv[i] = const_cast<char *>(args[i].c_str());
168 argv[args.size()] = nullptr;
170 if (pipe(fd_in) != 0) {
171 throw SysError("failed to open pipe for child process' stdin");
173 if (pipe(fd_out) != 0) {
174 throw SysError("failed to open pipe for child process' stdout");
176 if (pipe(fd_err) != 0) {
177 throw SysError("failed to open pipe for child process' stderr");
182 throw SysError("fork");
183 } else if (pid == 0) {
185 if (dup2(fd_in[0], STDIN_FILENO) == -1) {
188 if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
191 if (dup2(fd_err[1], STDERR_FILENO) == -1) {
202 execve(path, argv, envp);
203 // if execve returns, something bad happened
216 Process::Impl::~Impl() {
220 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
223 if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
224 throw runtime_error("failed to write to child process' input stream");
228 int written = write(fd_in[1], buffer, max_len);
230 if (errno == EAGAIN) {
233 throw SysError("failed to write to child process' input stream");
240 size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
243 if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
244 throw runtime_error("failed to read from child process' output stream");
248 int ret = read(fd_out[0], buffer, max_len);
250 if (errno == EAGAIN) {
253 throw SysError("failed to read from child process' output stream");
260 size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
263 if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
264 throw runtime_error("failed to read from child process' error stream");
268 int ret = read(fd_err[0], buffer, max_len);
270 if (errno == EAGAIN) {
273 throw SysError("failed to read from child process' error stream");
280 void Process::Impl::Terminate() {
282 if (!TerminateProcess(pi.hProcess, -1)) {
284 if (kill(pid, SIGTERM) == -1) {
286 throw SysError("failed to terminate child process");
290 int Process::Impl::Join() {
292 CloseHandle(fd_in[1]);
293 CloseHandle(fd_out[0]);
294 CloseHandle(fd_err[0]);
297 WaitForSingleObject(pi.hProcess, INFINITE);
298 GetExitCodeProcess(pi.hProcess, &exit_code);
299 CloseHandle(pi.hProcess);
300 CloseHandle(pi.hThread);
303 // close streams before waiting on child termination
310 int result = waitpid(pid, &status, 0);
312 throw SysError("error waiting on child process");
315 // should in theory only happen with WNOHANG set
318 if (WIFEXITED(status)) {
319 // autonomous termination
320 return WEXITSTATUS(status);
322 if (WIFSIGNALED(status)) {
323 // signalled termination
324 return WTERMSIG(status);
326 // otherwise, child probably signalled stop/continue, which we
327 // don't care about (please don't tell youth welfare), so try again