| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "minios/process_manager.h" |
| |
| #include <base/logging.h> |
| #include <base/posix/eintr_wrapper.h> |
| |
| using std::string; |
| using std::vector; |
| |
| namespace { |
| |
| bool LaunchProcess(const vector<string>& cmd, |
| int output_pipe, |
| brillo::Process* proc) { |
| for (const string& arg : cmd) |
| proc->AddArg(arg); |
| |
| proc->RedirectUsingPipe(output_pipe, false); |
| proc->SetCloseUnusedFileDescriptors(true); |
| proc->RedirectUsingPipe(STDOUT_FILENO, false); |
| return proc->Start(); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<brillo::Process> ProcessManager::CreateProcess( |
| const vector<string>& cmd, |
| const ProcessManagerInterface::IORedirection& io_redirection) { |
| std::unique_ptr<brillo::Process> process(new brillo::ProcessImpl); |
| for (const auto& arg : cmd) |
| process->AddArg(arg); |
| if (!io_redirection.input.empty()) |
| process->RedirectInput(io_redirection.input); |
| if (!io_redirection.output.empty()) |
| process->RedirectOutput(io_redirection.output); |
| return process; |
| } |
| |
| int ProcessManager::RunCommand( |
| const vector<string>& cmd, |
| const ProcessManagerInterface::IORedirection& io_redirection) { |
| auto process = CreateProcess(cmd, io_redirection); |
| return process->Run(); |
| } |
| |
| bool ProcessManager::RunBackgroundCommand( |
| const vector<string>& cmd, |
| const ProcessManagerInterface::IORedirection& io_redirection, |
| pid_t* pid) { |
| auto process = CreateProcess(cmd, io_redirection); |
| if (!process->Start()) |
| return false; |
| *pid = process->pid(); |
| // Need to release the process so it's not destructed at return. |
| process->Release(); |
| return true; |
| } |
| |
| bool ProcessManager::RunCommandWithOutput(const vector<string>& cmd, |
| int* return_code, |
| string* stdout_out, |
| string* stderr_out) { |
| brillo::ProcessImpl proc; |
| if (!LaunchProcess(cmd, STDERR_FILENO, &proc)) { |
| LOG(ERROR) << "Failed to launch subprocess"; |
| return false; |
| } |
| |
| // Read from both stdout and stderr individually. |
| int stdout_fd = proc.GetPipe(STDOUT_FILENO); |
| int stderr_fd = proc.GetPipe(STDERR_FILENO); |
| vector<char> buffer(32 * 1024); |
| bool stdout_closed = false, stderr_closed = false; |
| while (!stdout_closed || !stderr_closed) { |
| if (!stdout_closed) { |
| int rc = HANDLE_EINTR(read(stdout_fd, buffer.data(), buffer.size())); |
| if (rc <= 0) { |
| stdout_closed = true; |
| if (rc < 0) |
| PLOG(ERROR) << "Reading from child's stdout"; |
| } else if (stdout_out != nullptr) { |
| stdout_out->append(buffer.data(), rc); |
| } |
| } |
| |
| if (!stderr_closed) { |
| int rc = HANDLE_EINTR(read(stderr_fd, buffer.data(), buffer.size())); |
| if (rc <= 0) { |
| stderr_closed = true; |
| if (rc < 0) |
| PLOG(ERROR) << "Reading from child's stderr"; |
| } else if (stderr_out != nullptr) { |
| stderr_out->append(buffer.data(), rc); |
| } |
| } |
| } |
| |
| // At this point, the subprocess already closed the output, so we only need to |
| // wait for it to finish. |
| int proc_return_code = proc.Wait(); |
| if (return_code) |
| *return_code = proc_return_code; |
| return proc_return_code != brillo::Process::kErrorExitStatus; |
| } |