#include #include #include #include #include #include #include #include #include namespace fc { namespace bp = boost::process; namespace io = boost::iostreams; fc::path find_executable_in_path( const fc::string name ) { try { return fc::string(bp::find_executable_in_path( std::string(name), "" )); } catch (...) { const char* p = std::getenv("PATH"); FC_THROW( "Unable to find executable ${exe} in path.", ("exe", name) ("inner", fc::except_str() ) ("PATH", fc::string(p!=nullptr?p:"") ) ); } return fc::path(); } class process::impl { public: impl() :stat( fc::asio::default_io_service() ){} ~impl() { try { if( _in ) { _in->close(); } if( _exited.valid() && !_exited.ready()) { //child->terminate(); _exited.wait(); } } catch(...) { wlog( "caught exception cleaning up process: ${except_str}", ("except_str",fc::except_str()) ); } } std::shared_ptr child; std::shared_ptr _inp; std::shared_ptr _outp; std::shared_ptr _errp; buffered_istream_ptr _out; buffered_istream_ptr _err; buffered_ostream_ptr _in; bp::status stat; bp::context pctx; fc::future _exited; }; process::process() :my( new process::impl() ){} process::~process(){} iprocess& process::exec( const fc::path& exe, std::vector args, const fc::path& work_dir, int opt ) { my->pctx.work_dir = work_dir.string(); my->pctx.suppress_console = (opt & suppress_console) != 0; if( opt&open_stdout) my->pctx.streams[boost::process::stdout_id] = bp::behavior::async_pipe(); else my->pctx.streams[boost::process::stdout_id] = bp::behavior::null(); if( opt& open_stderr ) my->pctx.streams[boost::process::stderr_id] = bp::behavior::async_pipe(); else my->pctx.streams[boost::process::stderr_id] = bp::behavior::null(); if( opt& open_stdin ) my->pctx.streams[boost::process::stdin_id] = bp::behavior::async_pipe(); else my->pctx.streams[boost::process::stdin_id] = bp::behavior::close(); /* std::vector a; a.reserve(size_t(args.size())); for( uint32_t i = 0; i < args.size(); ++i ) { a.push_back( fc::move(args[i]) ); } */ my->child.reset( new bp::child( bp::create_child( exe.string(), fc::move(args), my->pctx ) ) ); if( opt & open_stdout ) { bp::handle outh = my->child->get_handle( bp::stdout_id ); my->_outp.reset( new bp::pipe( fc::asio::default_io_service(), outh.release() ) ); } if( opt & open_stderr ) { bp::handle errh = my->child->get_handle( bp::stderr_id ); my->_errp.reset( new bp::pipe( fc::asio::default_io_service(), errh.release() ) ); } if( opt & open_stdin ) { bp::handle inh = my->child->get_handle( bp::stdin_id ); my->_inp.reset( new bp::pipe( fc::asio::default_io_service(), inh.release() ) ); } promise::ptr p(new promise("process")); my->stat.async_wait( my->child->get_id(), [=]( const boost::system::error_code& ec, int exit_code ) { //slog( "process::result %d", exit_code ); if( !ec ) { #ifdef BOOST_POSIX_API if( WIFEXITED(exit_code) ) p->set_value( WEXITSTATUS(exit_code) ); else { p->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "process exited with: ${message} ", ("message", strsignal(WTERMSIG(exit_code))) ) ) ) ); } #else p->set_value(exit_code); #endif } else { p->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "process exited with: ${message} ", ("message", boost::system::system_error(ec).what())) ) ) ); } }); if( opt & open_stdin ) my->_in = std::make_shared(std::make_shared>(my->_inp)); if( opt & open_stdout ) my->_out = std::make_shared(std::make_shared>(my->_outp)); if( opt & open_stderr ) my->_err = std::make_shared(std::make_shared>(my->_errp)); my->_exited = p; return *this; } /** * Forcefully kills the process. */ void process::kill() { my->child->terminate(); } /** * @brief returns a stream that writes to the process' stdin */ fc::buffered_ostream_ptr process::in_stream() { return my->_in; } /** * @brief returns a stream that reads from the process' stdout */ fc::buffered_istream_ptr process::out_stream() { return my->_out; } /** * @brief returns a stream that reads from the process' stderr */ fc::buffered_istream_ptr process::err_stream() { return my->_err; } int process::result(const microseconds& timeout /* = microseconds::maximum() */) { return my->_exited.wait(timeout); } }