/******************************************************************************** * Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * * copied verbatim in the file "LICENSE" * ********************************************************************************/ #include #include #include #include #include #include #include #include using namespace std; namespace bp = boost::process; namespace ba = boost::asio; namespace bs = boost::system; namespace fair { namespace mq { namespace tools { /** * Execute given command in forked process and capture stdout output * and exit code. * * @param[in] cmd Command to execute * @param[in] log_prefix How to prefix each captured output line with * @return Captured stdout output and exit code */ execute_result execute(const string& cmd, const string& prefix, const string& input) { execute_result result; stringstream out; // print full line thread-safe stringstream printCmd; printCmd << prefix << " " << cmd << "\n"; cout << printCmd.str() << flush; out << prefix << cmd << endl; ba::io_service ios; // containers for std_in ba::const_buffer inputBuffer(ba::buffer(input)); bp::async_pipe inputPipe(ios); // containers for std_out ba::streambuf outputBuffer; bp::async_pipe outputPipe(ios); // containers for std_err ba::streambuf errorBuffer; bp::async_pipe errorPipe(ios); const string delimiter = "\n"; ba::deadline_timer timer(ios, boost::posix_time::milliseconds(100)); // child process bp::child c(cmd, bp::std_out > outputPipe, bp::std_err > errorPipe, bp::std_in < inputPipe); // handle std_in with a delay if (input != "") { timer.async_wait([&](const bs::error_code& ec1) { if (!ec1) { ba::async_write(inputPipe, inputBuffer, [&](const bs::error_code& ec2, size_t /* n */) { if (!ec2) { // inputPipe.async_close(); } else { cout << prefix << "error in boost::asio::async_write: " << ec2.message() << endl; out << prefix << "error in boost::asio::async_write: " << ec2.message() << endl; } }); } else { cout << prefix << "error in boost::asio::deadline_timer.async_wait: " << ec1.message() << endl; out << prefix << "error in boost::asio::deadline_timer.async_wait: " << ec1.message() << endl; } }); } // handle std_out line by line function onStdOut = [&](const bs::error_code& ec, size_t /* n */) { if (!ec) { istream is(&outputBuffer); string line; getline(is, line); stringstream printLine; printLine << prefix << line << "\n"; cout << printLine.str() << flush; out << prefix << line << endl; ba::async_read_until(outputPipe, outputBuffer, delimiter, onStdOut); } else { if (ec == ba::error::eof) { // outputPipe.async_close(); } else { cout << prefix << ec.message() << endl; out << prefix << ec.message() << endl; } } }; ba::async_read_until(outputPipe, outputBuffer, delimiter, onStdOut); // handle std_err line by line function onStdErr = [&](const bs::error_code& ec, size_t /* n */) { if (!ec) { istream is(&errorBuffer); string line; getline(is, line); stringstream printLine; printLine << prefix << line << "\n"; cerr << printLine.str() << flush; out << prefix << "error: " << line << endl; ba::async_read_until(errorPipe, errorBuffer, delimiter, onStdErr); } else { if (ec == ba::error::eof) { // errorPipe.async_close(); } else { cout << prefix << ec.message() << endl; out << prefix << ec.message() << endl; } } }; ba::async_read_until(errorPipe, errorBuffer, delimiter, onStdErr); ios.run(); c.wait(); result.exit_code = c.exit_code(); stringstream exitCode; exitCode << prefix << " fair::mq::tools::execute: exit code: " << result.exit_code << "\n"; cout << exitCode.str() << flush; out << prefix << " fair::mq::tools::execute: exit code: " << result.exit_code << endl; result.console_out = out.str(); return result; } } /* namespace tools */ } /* namespace mq */ } /* namespace fair */