11 #include "includes/subprocess.h"
12 #include "includes/process.h"
13 #include "includes/pipe_reader.h"
14 #include "includes/assert.h"
16 namespace linuxdeploy {
17 namespace subprocess {
18 subprocess::subprocess(std::initializer_list<std::string> args, subprocess_env_map_t env)
19 : subprocess(std::vector<std::string>(args), std::move(env)) {}
21 subprocess::subprocess(std::vector<std::string> args, subprocess_env_map_t env)
22 : args_(std::move(args)), env_(std::move(env)) {
24 util::assert::assert_not_empty(args_);
27 subprocess_result subprocess::run() const {
28 process proc{args_, env_};
30 // create pipe readers and empty buffers for both stdout and stderr
31 // we manage them in this (admittedly, kind of complex-looking) array so we can later easily perform the
32 // operations in a loop
33 std::array<std::pair<pipe_reader, subprocess_result_buffer_t>, 2> buffers{
34 std::make_pair(pipe_reader(proc.stdout_fd()), subprocess_result_buffer_t{}),
35 std::make_pair(pipe_reader(proc.stderr_fd()), subprocess_result_buffer_t{}),
39 for (auto& pair : buffers) {
40 // make code more readable
41 auto& reader = pair.first;
42 auto& buffer = pair.second;
44 // read some bytes into smaller intermediate buffer to prevent either of the pipes to overflow
45 // the results are immediately appended to the main buffer
46 subprocess_result_buffer_t intermediate_buffer(4096);
48 // (try to) read all available data from pipe
50 const auto bytes_read = reader.read(intermediate_buffer);
52 if (bytes_read == 0) {
56 // append to main buffer
57 buffer.reserve(buffer.size() + bytes_read);
58 std::copy(intermediate_buffer.begin(), (intermediate_buffer.begin() + bytes_read),
59 std::back_inserter(buffer));
63 // do-while might be a little more elegant, but we can save this one unnecessary sleep, so...
64 if (proc.is_running()) {
66 std::this_thread::sleep_for(std::chrono::milliseconds(50));
72 // make sure contents are null-terminated
73 buffers[0].second.emplace_back('\0');
74 buffers[1].second.emplace_back('\0');
76 auto exit_code = proc.close();
78 return subprocess_result{exit_code, buffers[0].second, buffers[1].second};
81 std::string subprocess::check_output() const {
82 const auto result = run();
84 if (result.exit_code() != 0) {
85 throw std::logic_error{"subprocess failed (exit code " + std::to_string(result.exit_code()) + ")"};
88 return result.stdout_string();