X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fapp%2Fproc.cpp;h=1373a9107ff8029f1a80ab39adb9f501e02bba70;hb=HEAD;hp=876eefc0527aaf110d215a3c0ec683c1cae6c966;hpb=0644360107ca50d681ae8b8cad608c7bc2ec7a40;p=blank.git diff --git a/src/app/proc.cpp b/src/app/proc.cpp index 876eefc..1373a91 100644 --- a/src/app/proc.cpp +++ b/src/app/proc.cpp @@ -9,6 +9,7 @@ # include # include # include +# include # include #endif @@ -23,18 +24,23 @@ namespace blank { struct Process::Impl { Impl( - const string &path_in, + const string &path, const Arguments &args, - const Environment &env); + char *const env[]); ~Impl(); + static Impl *EnvHelper( + const string &path, + const Arguments &args, + const Environment &env); + size_t WriteIn(const void *buffer, size_t max_len); void CloseIn(); - size_t ReadOut(void *buffer, size_t max_len); + size_t ReadOut(void *buffer, size_t max_len, int timeout); void CloseOut(); - size_t ReadErr(void *buffer, size_t max_len); + size_t ReadErr(void *buffer, size_t max_len, int timeout); void CloseErr(); void Terminate(); @@ -63,11 +69,18 @@ struct Process::Impl { }; +Process::Process( + const string &path, + const Arguments &args) +: impl(new Impl(path, args, nullptr)) { + +} + Process::Process( const string &path, const Arguments &args, const Environment &env) -: impl(new Impl(path, args, env)) { +: impl(Impl::EnvHelper(path, args, env)) { } @@ -84,16 +97,16 @@ void Process::CloseIn() { impl->CloseIn(); } -size_t Process::ReadOut(void *buffer, size_t max_len) { - return impl->ReadOut(buffer, max_len); +size_t Process::ReadOut(void *buffer, size_t max_len, int timeout) { + return impl->ReadOut(buffer, max_len, timeout); } void Process::CloseOut() { impl->CloseOut(); } -size_t Process::ReadErr(void *buffer, size_t max_len) { - return impl->ReadErr(buffer, max_len); +size_t Process::ReadErr(void *buffer, size_t max_len, int timeout) { + return impl->ReadErr(buffer, max_len, timeout); } void Process::CloseErr() { @@ -112,22 +125,29 @@ int Process::Join() { return impl->Join(); } +Process::Impl *Process::Impl::EnvHelper( + const string &path, + const Arguments &args, + const Environment &env +) { + char *envp[env.size() + 1]; + for (size_t i = 0; i < env.size(); ++i) { + envp[i] = const_cast(env[i].c_str()); + } + envp[env.size()] = nullptr; + return new Impl(path, args, envp); +} Process::Impl::Impl( const string &path_in, const Arguments &args, - const Environment &env) + char *const envp[]) : joined(false) , status(0) , in_closed(false) , out_closed(false) , err_closed(false) { const char *path = path_in.c_str(); - char *envp[env.size() + 1]; - for (size_t i = 0; i < env.size(); ++i) { - envp[i] = const_cast(env[i].c_str()); - } - envp[env.size()] = nullptr; #ifdef _WIN32 string cmdline; for (const auto &arg : args) { @@ -222,7 +242,11 @@ Process::Impl::Impl( close(fd_err[0]); close(fd_err[1]); - execve(path, argv, envp); + if (envp) { + execve(path, argv, envp); + } else { + execv(path, argv); + } // if execve returns, something bad happened exit(EXIT_FAILURE); @@ -274,14 +298,35 @@ void Process::Impl::CloseIn() { in_closed = true; } -size_t Process::Impl::ReadOut(void *buffer, size_t max_len) { +size_t Process::Impl::ReadOut(void *buffer, size_t max_len, int timeout) { #ifdef _WIN32 + // TODO: timeout implementation for windows child process I/O DWORD ret; if (!ReadFile(fd_out[0], buffer, max_len, &ret, nullptr)) { throw runtime_error("failed to read from child process' output stream"); } return ret; #else + if (timeout >= 0) { + fd_set read_set; + fd_set error_set; + FD_ZERO(&read_set); + FD_ZERO(&error_set); + FD_SET(fd_out[0], &read_set); + FD_SET(fd_out[0], &error_set); + timeval timer; + timer.tv_sec = timeout / 1000; + timer.tv_usec = (timeout % 1000) * 1000; + if (select(fd_out[0] + 1, &read_set, nullptr, &error_set, &timer) == -1) { + throw SysError("error waiting on child process' output stream"); + } + if (FD_ISSET(fd_out[0], &error_set)) { + throw runtime_error("error condition on child process' output stream"); + } + if (!FD_ISSET(fd_out[0], &read_set)) { + throw runtime_error("timeout while waiting on child process' output stream"); + } + } int ret = read(fd_out[0], buffer, max_len); if (ret < 0) { if (errno == EAGAIN) { @@ -306,14 +351,35 @@ void Process::Impl::CloseOut() { out_closed = true; } -size_t Process::Impl::ReadErr(void *buffer, size_t max_len) { +size_t Process::Impl::ReadErr(void *buffer, size_t max_len, int timeout) { #ifdef _WIN32 + // TODO: timeout implementation for windows child process I/O DWORD ret; if (!ReadFile(fd_err[0], buffer, max_len, &ret, nullptr)) { throw runtime_error("failed to read from child process' error stream"); } return ret; #else + if (timeout >= 0) { + fd_set read_set; + fd_set error_set; + FD_ZERO(&read_set); + FD_ZERO(&error_set); + FD_SET(fd_err[0], &read_set); + FD_SET(fd_err[0], &error_set); + timeval timer; + timer.tv_sec = timeout / 1000; + timer.tv_usec = (timeout % 1000) * 1000; + if (select(fd_err[0] + 1, &read_set, nullptr, &error_set, &timer) == -1) { + throw SysError("error waiting on child process' error stream"); + } + if (FD_ISSET(fd_err[0], &error_set)) { + throw runtime_error("error condition on child process' error stream"); + } + if (!FD_ISSET(fd_err[0], &read_set)) { + throw runtime_error("timeout while waiting on child process' error stream"); + } + } int ret = read(fd_err[0], buffer, max_len); if (ret < 0) { if (errno == EAGAIN) {