10 # include <sys/wait.h>
21 struct Process::Impl {
24 const string &path_in,
25 const vector<string> &args,
26 const vector<string> &env);
29 size_t WriteIn(const void *buffer, size_t max_len);
30 size_t ReadOut(void *buffer, size_t max_len);
31 size_t ReadErr(void *buffer, size_t max_len);
37 PROCESS_INFORMATION pi;
53 const vector<string> &args,
54 const vector<string> &env)
55 : impl(new Impl(path, args, env))
66 size_t Process::WriteIn(const void *buffer, size_t max_len) {
67 return impl->WriteIn(buffer, max_len);
70 size_t Process::ReadOut(void *buffer, size_t max_len) {
71 return impl->ReadOut(buffer, max_len);
74 size_t Process::ReadErr(void *buffer, size_t max_len) {
75 return impl->ReadErr(buffer, max_len);
78 void Process::Terminate() {
88 status = impl->Join();
96 const string &path_in,
97 const vector<string> &args,
98 const vector<string> &env
100 const char *path = path_in.c_str();
101 char *envp[env.size() + 1];
102 for (size_t i = 0; i < env.size(); ++i) {
103 envp[i] = const_cast<char *>(env[i].c_str());
105 envp[env.size()] = nullptr;
108 for (const auto &arg : args) {
114 SECURITY_ATTRIBUTES sa;
115 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
116 sa.bInheritHandle = true;
117 sa.lpSecurityDescriptor = nullptr;
118 if (!CreatePipe(&fd_in[0], &fd_in[1], &sa, 0)) {
119 throw runtime_error("failed to open pipe for child process' stdin");
121 if (!SetHandleInformation(fd_in[1], HANDLE_FLAG_INHERIT, 0)) {
122 throw runtime_error("failed to stop child process from inheriting stdin write handle");
124 if (!CreatePipe(&fd_out[0], &fd_out[1], &sa, 0)) {
125 throw runtime_error("failed to open pipe for child process' stdout");
127 if (!SetHandleInformation(fd_out[0], HANDLE_FLAG_INHERIT, 0)) {
128 throw runtime_error("failed to stop child process from inheriting stdout read handle");
130 if (!CreatePipe(&fd_err[0], &fd_err[1], &sa, 0)) {
131 throw runtime_error("failed to open pipe for child process' stderr");
133 if (!SetHandleInformation(fd_err[0], HANDLE_FLAG_INHERIT, 0)) {
134 throw runtime_error("failed to stop child process from inheriting stderr read handle");
138 ZeroMemory(&si, sizeof(STARTUPINFO));
139 si.cb = sizeof(STARTUPINFO);
140 si.hStdError = fd_err[1];
141 si.hStdOutput = fd_out[1];
142 si.hStdInput = fd_in[0];
143 si.dwFlags |= STARTF_USESTDHANDLES;
144 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
158 throw runtime_error("CreateProcess");
161 char *argv[args.size() + 1];
162 for (size_t i = 0; i < args.size(); ++i) {
163 // I was promised exec won't modify my characters
164 argv[i] = const_cast<char *>(args[i].c_str());
166 argv[args.size()] = nullptr;
168 if (pipe(fd_in) != 0) {
169 throw runtime_error("failed to open pipe for child process' stdin");
171 if (pipe(fd_out) != 0) {
172 throw runtime_error("failed to open pipe for child process' stdout");
174 if (pipe(fd_err) != 0) {
175 throw runtime_error("failed to open pipe for child process' stderr");
180 throw runtime_error("fork");
181 } else if (pid == 0) {
183 if (dup2(fd_in[0], STDIN_FILENO) == -1) {
186 if (dup2(fd_out[1], STDOUT_FILENO) == -1) {
189 if (dup2(fd_err[1], STDERR_FILENO) == -1) {
200 execve(path, argv, envp);
201 // if execve returns, something bad happened
214 Process::Impl::~Impl() {
218 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
221 if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
222 throw runtime_error("failed to write to child process' input stream");
226 int written = write(fd_in[1], buffer, max_len);
228 if (errno == EAGAIN) {
231 throw runtime_error("failed to write to child process' input stream");
238 size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
241 if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
242 throw runtime_error("failed to read from child process' output stream");
246 int ret = read(fd_out[0], buffer, max_len);
248 if (errno == EAGAIN) {
251 throw runtime_error("failed to read from child process' output stream");
258 size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
261 if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
262 throw runtime_error("failed to read from child process' error stream");
266 int ret = read(fd_err[0], buffer, max_len);
268 if (errno == EAGAIN) {
271 throw runtime_error("failed to read from child process' error stream");
278 void Process::Impl::Terminate() {
280 if (!TerminateProcess(pi.hProcess, -1)) {
282 if (kill(pid, SIGTERM) == -1) {
284 throw runtime_error("failed to terminate child process");
288 int Process::Impl::Join() {
290 CloseHandle(fd_in[1]);
291 CloseHandle(fd_out[0]);
292 CloseHandle(fd_err[0]);
295 WaitForSingleObject(pi.hProcess, INFINITE);
296 GetExitCodeProcess(pi.hProcess, &exit_code);
297 CloseHandle(pi.hProcess);
298 CloseHandle(pi.hThread);
301 // close streams before waiting on child termination
308 int result = waitpid(pid, &status, 0);
310 throw runtime_error("error waiting on child process");
312 if (result == pid && WIFEXITED(status)) {
313 return WEXITSTATUS(status);
315 // otherwise, child probably signalled, which we don't care
316 // about (please don't tell youth welfare), so try again