#include "Process.hpp"
+#include "error.hpp"
+
#ifdef _WIN32
-# error "TODO: windows implementation of Process"
+# include <tchar.h>
+# include <windows.h>
#else
-# include <cstdio>
# include <fcntl.h>
+# include <signal.h>
# include <unistd.h>
# include <sys/wait.h>
#endif
+#include <cstdio>
#include <stdexcept>
using namespace std;
Impl(
const string &path_in,
- const vector<string> &args,
- const vector<string> &env);
+ const Arguments &args,
+ const Environment &env);
~Impl();
size_t WriteIn(const void *buffer, size_t max_len);
size_t ReadOut(void *buffer, size_t max_len);
size_t ReadErr(void *buffer, size_t max_len);
+ void Terminate();
int Join();
#ifdef _WIN32
Process::Process(
const string &path,
- const vector<string> &args,
- const vector<string> &env)
+ const Arguments &args,
+ const Environment &env)
: impl(new Impl(path, args, env))
, joined(false)
, status(0) {
return impl->ReadErr(buffer, max_len);
}
+void Process::Terminate() {
+ if (!joined) {
+ impl->Terminate();
+ }
+}
+
int Process::Join() {
if (joined) {
return status;
Process::Impl::Impl(
const string &path_in,
- const vector<string> &args,
- const vector<string> &env
+ const Arguments &args,
+ const Environment &env
) {
const char *path = path_in.c_str();
char *envp[env.size() + 1];
argv[args.size()] = nullptr;
if (pipe(fd_in) != 0) {
- throw runtime_error("failed to open pipe for child process' stdin");
+ throw SysError("failed to open pipe for child process' stdin");
}
if (pipe(fd_out) != 0) {
- throw runtime_error("failed to open pipe for child process' stdout");
+ throw SysError("failed to open pipe for child process' stdout");
}
if (pipe(fd_err) != 0) {
- throw runtime_error("failed to open pipe for child process' stderr");
+ throw SysError("failed to open pipe for child process' stderr");
}
pid = fork();
if (pid == -1) {
- throw runtime_error("fork");
+ throw SysError("fork");
} else if (pid == 0) {
if (dup2(fd_in[0], STDIN_FILENO) == -1) {
if (errno == EAGAIN) {
return 0;
} else {
- throw runtime_error("failed to write to child process' input stream");
+ throw SysError("failed to write to child process' input stream");
}
}
return written;
if (errno == EAGAIN) {
return 0;
} else {
- throw runtime_error("failed to read from child process' output stream");
+ throw SysError("failed to read from child process' output stream");
}
}
return ret;
if (errno == EAGAIN) {
return 0;
} else {
- throw runtime_error("failed to read from child process' error stream");
+ throw SysError("failed to read from child process' error stream");
}
}
return ret;
#endif
}
+void Process::Impl::Terminate() {
+#ifdef _WIN32
+ if (!TerminateProcess(pi.hProcess, -1)) {
+#else
+ if (kill(pid, SIGTERM) == -1) {
+#endif
+ throw SysError("failed to terminate child process");
+ }
+}
+
int Process::Impl::Join() {
#ifdef _WIN32
CloseHandle(fd_in[1]);
int status;
int result = waitpid(pid, &status, 0);
if (result == -1) {
- throw runtime_error("error waiting on child process");
+ throw SysError("error waiting on child process");
+ }
+ if (result != pid) {
+ // should in theory only happen with WNOHANG set
+ continue;
}
- if (result == pid && WIFEXITED(status)) {
+ if (WIFEXITED(status)) {
+ // autonomous termination
return WEXITSTATUS(status);
}
- // otherwise, child probably signalled, which we don't care
- // about (please don't tell youth welfare), so try again
+ if (WIFSIGNALED(status)) {
+ // signalled termination
+ return WTERMSIG(status);
+ }
+ // otherwise, child probably signalled stop/continue, which we
+ // don't care about (please don't tell youth welfare), so try again
}
#endif
}