diff --git a/main b/main index b299c97..e09238b 100755 Binary files a/main and b/main differ diff --git a/subprocess.hpp b/subprocess.hpp index b02e64f..b03a049 100755 --- a/subprocess.hpp +++ b/subprocess.hpp @@ -18,6 +18,7 @@ extern "C" { #include #include #include + #include } namespace subprocess { @@ -205,6 +206,16 @@ struct close_fds { bool close_all = false; }; +struct session_leader { + session_leader(bool sl): leader_(sl) {} + bool leader_ = false; +}; + +struct shell { + shell(bool s): shell_(s) {} + bool shell_ = false; +}; + struct string_arg { string_arg(const char* arg): arg_value(arg) {} @@ -308,6 +319,43 @@ struct error int wr_ch_ = -1; }; +// Impoverished, meager, needy, truly needy +// version of type erasure to store function pointers +// needed to provide the functionality of preexec_func +// ATTN: Can be used only to execute functions with no +// arguments and returning void. +// Could have used more efficient methods, ofcourse, but +// that wont yield me the consistent syntax which I am +// aiming for. If you know, then please do let me know. + +class preexec_func +{ +public: + preexec_func() {} + + template + preexec_func(Func f): holder_(new FuncHolder(f)) + {} + + void operator()() { + (*holder_)(); + } + +private: + struct HolderBase { + virtual void operator()() const; + }; + template + struct FuncHolder: HolderBase { + FuncHolder(T func): func_(func) {} + void operator()() const override {} + // The function pointer/reference + T func_; + }; + + std::unique_ptr holder_ = nullptr; +}; + // ~~~~ End Popen Args ~~~~ // @@ -382,10 +430,13 @@ struct ArgumentDeducer void set_option(bufsize&& bsiz); void set_option(environment&& env); void set_option(defer_spawn&& defer); + void set_option(shell&& sh); void set_option(input&& inp); void set_option(output&& out); void set_option(error&& err); void set_option(close_fds&& cfds); + void set_option(preexec_func&& prefunc); + void set_option(session_leader&& sleader); private: Popen* popen_ = nullptr; @@ -570,6 +621,10 @@ public: int poll() throw(OSError); + // Does not fail, Caller is expected to recheck the + // status with a call to poll() + void kill(int sig_num = 9); + void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); } void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); } @@ -615,9 +670,14 @@ private: bool defer_process_start_ = false; bool close_fds_ = false; + bool has_preexec_fn_ = false; + bool shell_ = false; + bool session_leader_ = false; + std::string exe_name_; std::string cwd_; std::map env_; + preexec_func preexec_fn_; // Command in string format std::string args_; @@ -713,12 +773,22 @@ int Popen::poll() throw (OSError) return retcode_; } +void Popen::kill(int sig_num) +{ + if (session_leader_) killpg(child_pid_, sig_num); + else ::kill(child_pid_, sig_num); +} + void Popen::execute_process() throw (CalledProcessError, OSError) { int err_rd_pipe, err_wr_pipe; std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec(); + if (shell_) { + vargs_.insert(vargs_.begin(), {"/bin/sh", "-c"}); + } + if (!exe_name_.length()) { exe_name_ = vargs_[0]; } @@ -799,6 +869,14 @@ namespace detail { popen_->defer_process_start_ = defer.defer; } + void ArgumentDeducer::set_option(shell&& sh) { + popen_->shell_ = sh.shell_; + } + + void ArgumentDeducer::set_option(session_leader&& sleader) { + popen_->session_leader_ = sleader.leader_; + } + void ArgumentDeducer::set_option(input&& inp) { if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_; if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_; @@ -824,6 +902,11 @@ namespace detail { popen_->close_fds_ = cfds.close_all; } + void ArgumentDeducer::set_option(preexec_func&& prefunc) { + popen_->preexec_fn_ = std::move(prefunc); + popen_->has_preexec_fn_ = true; + } + void Child::execute_child() { int sys_ret = -1; @@ -886,6 +969,15 @@ namespace detail { if (sys_ret == -1) throw OSError("chdir failed", errno); } + if (parent_->has_preexec_fn_) { + parent_->preexec_fn_(); + } + + if (parent_->session_leader_) { + sys_ret = setsid(); + if (sys_ret == -1) throw OSError("setsid failed", errno); + } + // Replace the current image with the executable if (parent_->env_.size()) { for (auto& kv : parent_->env_) { @@ -1137,7 +1229,7 @@ OutBuffer check_output(const std::string& arg, Args&&... args) // Piping Support template -// Args expected to be std::initializer_list +// Args expected to be flat commands using string instead of initializer_list OutBuffer pipeline(Args&&... args) { std::vector pcmds; diff --git a/test/test_cat b/test/test_cat index c7df47f..52d2c4a 100755 Binary files a/test/test_cat and b/test/test_cat differ diff --git a/test/test_cat.cc b/test/test_cat.cc index 3905b52..8019788 100755 --- a/test/test_cat.cc +++ b/test/test_cat.cc @@ -23,8 +23,16 @@ void test_cat_file_redirection() std::cout << "END_TEST" << std::endl; } +void test_buffer_growth() +{ + auto obuf = sp::check_output({"cat", "../subprocess.hpp"}); + std::cout << obuf.length << std::endl; + assert (obuf.length > sp::DEFAULT_BUF_CAP_BYTES); +} + int main() { test_cat_pipe_redirection(); test_cat_file_redirection(); + test_buffer_growth(); return 0; } diff --git a/test/test_subprocess.cc b/test/test_subprocess.cc index e857a04..5d186c7 100755 --- a/test/test_subprocess.cc +++ b/test/test_subprocess.cc @@ -27,9 +27,16 @@ void test_easy_piping() std::cout << res.buf.data() << std::endl; } +void test_shell() +{ + auto obuf = check_output({"ls", "-l"}, shell{false}); + std::cout << obuf.buf.data() << std::endl; +} + int main() { test_input(); test_piping(); test_easy_piping(); + test_shell(); return 0; }