]> git.localhorst.tv Git - blank.git/blobdiff - src/app/proc.cpp
also join processes terminated by signal
[blank.git] / src / app / proc.cpp
index 8809856c2a8c3640f45c23e3737e3a79702b0d5e..add16d1787a4779068b6ce36b06be9c9d1d9e194 100644 (file)
@@ -1,14 +1,18 @@
 #include "Process.hpp"
 
-#ifdef __WIN32
-#  error "TODO: windows implementation of Process"
+#include "error.hpp"
+
+#ifdef _WIN32
+#  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;
@@ -20,17 +24,18 @@ struct Process::Impl {
 
        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
+#ifdef _WIN32
        PROCESS_INFORMATION pi;
        HANDLE fd_in[2];
        HANDLE fd_out[2];
@@ -47,8 +52,8 @@ struct Process::Impl {
 
 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) {
@@ -72,6 +77,12 @@ size_t Process::ReadErr(void *buffer, size_t max_len) {
        return impl->ReadErr(buffer, max_len);
 }
 
+void Process::Terminate() {
+       if (!joined) {
+               impl->Terminate();
+       }
+}
+
 int Process::Join() {
        if (joined) {
                return status;
@@ -85,8 +96,8 @@ int Process::Join() {
 
 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];
@@ -94,7 +105,7 @@ Process::Impl::Impl(
                envp[i] = const_cast<char *>(env[i].c_str());
        }
        envp[env.size()] = nullptr;
-#ifdef __WIN32
+#ifdef _WIN32
        string cmdline;
        for (const auto &arg : args) {
                cmdline += '"';
@@ -157,18 +168,18 @@ Process::Impl::Impl(
        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) {
@@ -207,7 +218,7 @@ Process::Impl::~Impl() {
 }
 
 size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
-#ifdef __WIN32
+#ifdef _WIN32
        DWORD written;
        if (!WriteFile(fd_in[1], buffer, max_len, &written, nullptr)) {
                throw runtime_error("failed to write to child process' input stream");
@@ -219,7 +230,7 @@ size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
                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;
@@ -227,7 +238,7 @@ size_t Process::Impl::WriteIn(const void *buffer, size_t max_len) {
 }
 
 size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
-#ifdef __WIN32
+#ifdef _WIN32
        DWORD ret;
        if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) {
                throw runtime_error("failed to read from child process' output stream");
@@ -239,7 +250,7 @@ size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
                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;
@@ -247,7 +258,7 @@ size_t Process::Impl::ReadOut(void *buffer, size_t max_len) {
 }
 
 size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
-#ifdef __WIN32
+#ifdef _WIN32
        DWORD ret;
        if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) {
                throw runtime_error("failed to read from child process' error stream");
@@ -259,15 +270,25 @@ size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
                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
+#ifdef _WIN32
        CloseHandle(fd_in[1]);
        CloseHandle(fd_out[0]);
        CloseHandle(fd_err[0]);
@@ -288,13 +309,22 @@ int Process::Impl::Join() {
                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
 }