#include "process.hpp" #include #include #include #include namespace TinyProcessLib { Process::Data::Data() noexcept : id(-1) {} Process::Process(std::function function, std::function read_stdout, std::function read_stderr, bool open_stdin, size_t buffer_size) noexcept : closed(true), read_stdout(std::move(read_stdout)), read_stderr(std::move(read_stderr)), open_stdin(open_stdin), buffer_size(buffer_size) { open(function); async_read(); } Process::id_type Process::open(std::function function) noexcept { if(open_stdin) stdin_fd=std::unique_ptr(new fd_type); if(read_stdout) stdout_fd=std::unique_ptr(new fd_type); if(read_stderr) stderr_fd=std::unique_ptr(new fd_type); int stdin_p[2], stdout_p[2], stderr_p[2]; if(stdin_fd && pipe(stdin_p)!=0) return -1; if(stdout_fd && pipe(stdout_p)!=0) { if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);} return -1; } if(stderr_fd && pipe(stderr_p)!=0) { if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);} if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);} return -1; } id_type pid = fork(); if (pid < 0) { if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);} if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);} if(stderr_fd) {close(stderr_p[0]);close(stderr_p[1]);} return pid; } else if (pid == 0) { if(stdin_fd) dup2(stdin_p[0], 0); if(stdout_fd) dup2(stdout_p[1], 1); if(stderr_fd) dup2(stderr_p[1], 2); if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);} if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);} if(stderr_fd) {close(stderr_p[0]);close(stderr_p[1]);} //Based on http://stackoverflow.com/a/899533/3808293 int fd_max=static_cast(sysconf(_SC_OPEN_MAX)); // truncation is safe for(int fd=3;fd( new char[buffer_size] ); ssize_t n; while ((n=read(*stdout_fd, buffer.get(), buffer_size)) > 0) read_stdout(buffer.get(), static_cast(n)); }); } if(stderr_fd) { stderr_thread=std::thread([this](){ auto buffer = std::unique_ptr( new char[buffer_size] ); ssize_t n; while ((n=read(*stderr_fd, buffer.get(), buffer_size)) > 0) read_stderr(buffer.get(), static_cast(n)); }); } } int Process::get_exit_status() noexcept { if(data.id<=0) return -1; int exit_status; waitpid(data.id, &exit_status, 0); { std::lock_guard lock(close_mutex); closed=true; } close_fds(); if(exit_status>=256) exit_status=exit_status>>8; return exit_status; } bool Process::try_get_exit_status(int &exit_status) noexcept { if(data.id<=0) return false; id_type p = waitpid(data.id, &exit_status, WNOHANG); if (p == 0) return false; { std::lock_guard lock(close_mutex); closed=true; } close_fds(); if(exit_status>=256) exit_status=exit_status>>8; return true; } void Process::close_fds() noexcept { if(stdout_thread.joinable()) stdout_thread.join(); if(stderr_thread.joinable()) stderr_thread.join(); if(stdin_fd) close_stdin(); if(stdout_fd) { if(data.id>0) close(*stdout_fd); stdout_fd.reset(); } if(stderr_fd) { if(data.id>0) close(*stderr_fd); stderr_fd.reset(); } } bool Process::write(const char *bytes, size_t n) { if(!open_stdin) throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process."); std::lock_guard lock(stdin_mutex); if(stdin_fd) { if(::write(*stdin_fd, bytes, n)>=0) { return true; } else { return false; } } return false; } void Process::close_stdin() noexcept { std::lock_guard lock(stdin_mutex); if(stdin_fd) { if(data.id>0) close(*stdin_fd); stdin_fd.reset(); } } void Process::kill(bool force) noexcept { std::lock_guard lock(close_mutex); if(data.id>0 && !closed) { if(force) ::kill(-data.id, SIGTERM); else ::kill(-data.id, SIGINT); } } void Process::kill(id_type id, bool force) noexcept { if(id<=0) return; if(force) ::kill(-id, SIGTERM); else ::kill(-id, SIGINT); } } // TinyProsessLib