diff --git a/src/plugin/plugin_process_handler.cpp b/src/plugin/plugin_process_handler.cpp index 4825ca4..1a4a4dc 100644 --- a/src/plugin/plugin_process_handler.cpp +++ b/src/plugin/plugin_process_handler.cpp @@ -1,6 +1,7 @@ // system headers #include #include +#include // local headers #include @@ -15,7 +16,8 @@ namespace linuxdeploy { namespace plugin { using namespace core::log; - plugin_process_handler::plugin_process_handler(std::string name, bf::path path) : name_(std::move(name)), path_(std::move(path)) {} + plugin_process_handler::plugin_process_handler(std::string name, bf::path path) : name_(std::move(name)), + path_(std::move(path)) {} int plugin_process_handler::run(const bf::path& appDir) const { // prepare arguments and environment variables @@ -35,62 +37,80 @@ namespace linuxdeploy { // however, we just dump everything we receive directly in the log, using our ĺogging framework // we store an ldLog instance per stream so we can just send all data into those, which allows us to get away // with relatively small buffers (we don't have to cache complete lines or alike) - // parameter order: pipe reader, log type (used in prefix), ldLog instance, first message - std::array, 2> pipes_to_be_logged{ - std::make_tuple(pipe_reader(proc.stdout_fd()), "stdout", ldLog{}, true), - std::make_tuple(pipe_reader(proc.stderr_fd()), "stderr", ldLog{}, true), + class pipe_to_be_logged { + public: + pipe_reader reader_; + std::string stream_name_; + ldLog log_; + bool print_prefix_in_next_iteration_; + + pipe_to_be_logged(int pipe_fd, std::string stream_name) : reader_(pipe_fd), + stream_name_(std::move(stream_name)), + log_(), + print_prefix_in_next_iteration_(true) {} + }; + + std::array pipes_to_be_logged{ + pipe_to_be_logged(proc.stdout_fd(), "stdout"), + pipe_to_be_logged(proc.stderr_fd(), "stderr"), }; for (;;) { - for (auto& tuple : pipes_to_be_logged) { - // make code in this loop more readable - auto& reader = std::get<0>(tuple); - const auto& stream_name = std::get<1>(tuple); - auto& log = std::get<2>(tuple); - auto& is_first_message = std::get<3>(tuple); - - const auto log_prefix = "[" + name_ + "/" + stream_name + "] "; + for (auto& pipe_to_be_logged : pipes_to_be_logged) { + const auto log_prefix = "[" + name_ + "/" + pipe_to_be_logged.stream_name_ + "] "; // since we have our own ldLog instance for every pipe, we can get away with this rather small read buffer subprocess::subprocess_result_buffer_t intermediate_buffer(4096); // (try to) read from pipe - const auto bytes_read = reader.read(intermediate_buffer); + const auto bytes_read = pipe_to_be_logged.reader_.read(intermediate_buffer); // no action required in case we have not read anything from the pipe - if (bytes_read > 0) { - // special handling for the first message - if (is_first_message) { - log << log_prefix; - is_first_message = false; + if (bytes_read <= 0) { + continue; + } + + // we just trim the buffer to the bytes we read (makes the code below easier) + intermediate_buffer.resize(bytes_read); + + // all we have to do now is to look for CR or LF, send everything up to that location into the ldLog instance, + // write our prefix and then repeat + for (auto it = intermediate_buffer.begin(); it != intermediate_buffer.end(); ++it) { + if (pipe_to_be_logged.print_prefix_in_next_iteration_) { + pipe_to_be_logged.log_ << log_prefix; } - // we just trim the buffer to the bytes we read (makes the code below easier) - intermediate_buffer.resize(bytes_read); + const auto next_lf = std::find(it, intermediate_buffer.end(), '\n'); + const auto next_cr = std::find(it, intermediate_buffer.end(), '\r'); - // all we have to do now is to look for CR or LF, send everything up to that location into the ldLog instance, - // write our prefix and then repeat - for (auto it = intermediate_buffer.begin(); it != intermediate_buffer.end(); ++it) { - const auto next_lf = std::find(it, intermediate_buffer.end(), '\n'); - const auto next_cr = std::find(it, intermediate_buffer.end(), '\r'); + // we don't care which one goes first -- we pick the closest one, write everything up to it into our ldLog, + // then print our prefix and repeat that until there's nothing left in our buffer + auto next_control_char = std::min({next_lf, next_cr}); - // we don't care which one goes first -- we pick the closest one, write everything up to it into our ldLog, - // then print our prefix and repeat that until there's nothing left in our buffer - auto next_control_char = std::min({next_lf, next_cr}); + // if there is a control char, we remember this for the next iteration, where we print our + // log prefix + // in any case, we can write the remaining buffer contents into the ldLog object + pipe_to_be_logged.print_prefix_in_next_iteration_ = (next_control_char != + intermediate_buffer.end()); - if (next_control_char == intermediate_buffer.end()) { - break; - } + const auto distance_from_begin_to_it = std::distance(intermediate_buffer.begin(), it); + auto distance_from_it_to_next_cc = std::distance(it, next_control_char); - // need to make sure we include the control char in the write - log.write( - intermediate_buffer.data() + std::distance(intermediate_buffer.begin(), it), - std::distance(it, next_control_char) + 1 - ); + if (pipe_to_be_logged.print_prefix_in_next_iteration_) { + distance_from_it_to_next_cc++; + } - log << log_prefix; + // need to make sure we include the control char in the write + pipe_to_be_logged.log_.write( + intermediate_buffer.data() + distance_from_begin_to_it, + distance_from_it_to_next_cc + ); - it = next_control_char; + it = next_control_char; + + // TODO: should not be necessary, should be fixed in for loop + if (!pipe_to_be_logged.print_prefix_in_next_iteration_) { + break; } } }