blob: e36fdb03894f4ad00c551c0dff830ef545cd803c [file] [log] [blame]
Elly Jonesa44d22d2012-01-05 18:05:56 -05001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Elly Jones1c4c3a12011-12-20 15:01:59 -05002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenko262be3f2014-07-30 15:25:50 -07005#include "debugd/src/process_with_output.h"
Elly Jones1c4c3a12011-12-20 15:01:59 -05006
David Pursell300498a2014-11-03 15:47:36 -08007#include <signal.h>
8
Ben Chancd8fda42014-09-05 08:21:06 -07009#include <base/files/file_util.h>
Ben Chan9953a592014-02-05 23:32:00 -080010#include <base/strings/string_split.h>
Kevin Cernekee40dcb732018-03-20 15:08:51 -070011#include <base/strings/string_util.h>
Elly Jones1c4c3a12011-12-20 15:01:59 -050012
Eric Carusocc7106c2017-04-27 14:22:42 -070013#include "debugd/src/error_utils.h"
Hardik Goyalb09d6b02019-08-13 16:15:50 -070014#include "debugd/src/helper_utils.h"
Eric Carusocc7106c2017-04-27 14:22:42 -070015
Elly Jones1c4c3a12011-12-20 15:01:59 -050016namespace debugd {
17
David Pursell300498a2014-11-03 15:47:36 -080018namespace {
19
20const char kDBusErrorString[] = "org.chromium.debugd.error.RunProcess";
21const char kInitErrorString[] = "Process initialization failure.";
22const char kStartErrorString[] = "Process start failure.";
23const char kInputErrorString[] = "Process input write failure.";
24const char kPathLengthErrorString[] = "Path length is too long.";
25
David Pursell300498a2014-11-03 15:47:36 -080026} // namespace
27
28ProcessWithOutput::ProcessWithOutput()
Tom Hughesd6c2d392020-08-24 18:12:11 -070029 : separate_stderr_(false), use_minijail_(true) {}
Ben Chan78f89532014-08-29 09:35:09 -070030
Elly Jones1c4c3a12011-12-20 15:01:59 -050031ProcessWithOutput::~ProcessWithOutput() {
David Pursell300498a2014-11-03 15:47:36 -080032 outfile_.reset();
33 errfile_.reset();
Ben Chan78f89532014-08-29 09:35:09 -070034
Elly Jones1c4c3a12011-12-20 15:01:59 -050035 if (!outfile_path_.empty())
Ben Chan9953a592014-02-05 23:32:00 -080036 base::DeleteFile(outfile_path_, false); // not recursive
David Pursell300498a2014-11-03 15:47:36 -080037 if (!errfile_path_.empty())
38 base::DeleteFile(errfile_path_, false);
Elly Jones1c4c3a12011-12-20 15:01:59 -050039}
40
41bool ProcessWithOutput::Init() {
Paul Moy970e1122020-01-14 16:17:18 -070042 return Init({});
43}
44
45bool ProcessWithOutput::Init(
46 const std::vector<std::string>& minijail_extra_args) {
David Pursell300498a2014-11-03 15:47:36 -080047 if (use_minijail_) {
Paul Moy970e1122020-01-14 16:17:18 -070048 if (!SandboxedProcess::Init(minijail_extra_args))
David Pursell300498a2014-11-03 15:47:36 -080049 return false;
50 }
Ben Chan78f89532014-08-29 09:35:09 -070051
David Pursell300498a2014-11-03 15:47:36 -080052 outfile_.reset(base::CreateAndOpenTemporaryFile(&outfile_path_));
53 if (!outfile_.get()) {
Elly Jones1c4c3a12011-12-20 15:01:59 -050054 return false;
David Pursell300498a2014-11-03 15:47:36 -080055 }
56 if (separate_stderr_) {
57 errfile_.reset(base::CreateAndOpenTemporaryFile(&errfile_path_));
58 if (!errfile_.get()) {
59 return false;
60 }
61 }
Ben Chan78f89532014-08-29 09:35:09 -070062
Elly Jones1c4c3a12011-12-20 15:01:59 -050063 // We can't just RedirectOutput to the file we just created, since
64 // RedirectOutput uses O_CREAT | O_EXCL to open the target file (i.e., it'll
65 // fail if the file already exists). We can't CreateTemporaryFile() and then
66 // use that filename, since we'd have to remove it before using
67 // RedirectOutput, which exposes us to a /tmp race. Instead, bind outfile_'s
68 // fd to the subprocess's stdout and stderr.
David Pursell300498a2014-11-03 15:47:36 -080069 BindFd(fileno(outfile_.get()), STDOUT_FILENO);
70 BindFd(fileno(separate_stderr_ ? errfile_.get() : outfile_.get()),
71 STDERR_FILENO);
Elly Jones1c4c3a12011-12-20 15:01:59 -050072 return true;
73}
74
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080075bool ProcessWithOutput::GetOutputLines(std::vector<std::string>* output) const {
Elly Jones1c4c3a12011-12-20 15:01:59 -050076 std::string contents;
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080077 if (!GetOutput(&contents))
Elly Jones1c4c3a12011-12-20 15:01:59 -050078 return false;
Ben Chan78f89532014-08-29 09:35:09 -070079
Kevin Cernekee40dcb732018-03-20 15:08:51 -070080 // If the file contains "a\nb\n", base::SplitString() will return a vector
81 // {"a", "b", ""} because it treats "\n" as a delimiter, not an EOL
82 // character. Removing the final "\n" fixes this.
83 if (base::EndsWith(contents, "\n", base::CompareCase::SENSITIVE)) {
84 contents.pop_back();
85 }
86
Alex Vakulenkoe50371c2016-01-20 16:06:19 -080087 *output = base::SplitString(contents, "\n", base::KEEP_WHITESPACE,
88 base::SPLIT_WANT_ALL);
Elly Jones1c4c3a12011-12-20 15:01:59 -050089 return true;
90}
91
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080092bool ProcessWithOutput::GetOutput(std::string* output) const {
Ben Chan9953a592014-02-05 23:32:00 -080093 return base::ReadFileToString(outfile_path_, output);
Elly Jones1c4c3a12011-12-20 15:01:59 -050094}
95
David Pursell300498a2014-11-03 15:47:36 -080096bool ProcessWithOutput::GetError(std::string* error) {
97 return base::ReadFileToString(errfile_path_, error);
98}
99
100int ProcessWithOutput::RunProcess(const std::string& command,
101 const ArgList& arguments,
102 bool requires_root,
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800103 bool disable_sandbox,
David Pursell300498a2014-11-03 15:47:36 -0800104 const std::string* stdin,
105 std::string* stdout,
106 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700107 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800108 ProcessWithOutput process;
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800109 if (disable_sandbox) {
110 process.DisableSandbox();
111 } else if (requires_root) {
David Pursell300498a2014-11-03 15:47:36 -0800112 process.SandboxAs("root", "root");
113 }
Tom Hughesd6c2d392020-08-24 18:12:11 -0700114 return DoRunProcess(command, arguments, stdin, stdout, stderr, error,
115 &process);
David Pursell300498a2014-11-03 15:47:36 -0800116}
117
118int ProcessWithOutput::RunHelper(const std::string& helper,
119 const ArgList& arguments,
120 bool requires_root,
121 const std::string* stdin,
122 std::string* stdout,
123 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700124 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800125 std::string helper_path;
Hardik Goyalb09d6b02019-08-13 16:15:50 -0700126 if (!GetHelperPath(helper, &helper_path)) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700127 DEBUGD_ADD_ERROR(error, kDBusErrorString, kPathLengthErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800128 return kRunError;
129 }
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800130 return RunProcess(helper_path, arguments, requires_root,
131 false /* disable_sandbox */, stdin, stdout, stderr, error);
David Pursell300498a2014-11-03 15:47:36 -0800132}
133
134int ProcessWithOutput::RunProcessFromHelper(const std::string& command,
135 const ArgList& arguments,
136 const std::string* stdin,
137 std::string* stdout,
138 std::string* stderr) {
139 ProcessWithOutput process;
140 process.set_use_minijail(false);
141 process.SetSearchPath(true);
Tom Hughesd6c2d392020-08-24 18:12:11 -0700142 return DoRunProcess(command, arguments, stdin, stdout, stderr, nullptr,
143 &process);
David Pursell300498a2014-11-03 15:47:36 -0800144}
145
146int ProcessWithOutput::DoRunProcess(const std::string& command,
147 const ArgList& arguments,
148 const std::string* stdin,
149 std::string* stdout,
150 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700151 brillo::ErrorPtr* error,
David Pursell300498a2014-11-03 15:47:36 -0800152 ProcessWithOutput* process) {
153 process->set_separate_stderr(true);
154 if (!process->Init()) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700155 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInitErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800156 return kRunError;
157 }
158
159 process->AddArg(command);
160 for (const auto& argument : arguments) {
161 process->AddArg(argument);
162 }
163
164 int result = kRunError;
165 if (stdin) {
166 process->RedirectUsingPipe(STDIN_FILENO, true);
167 if (process->Start()) {
168 int stdin_fd = process->GetPipe(STDIN_FILENO);
169 // Kill the process if writing to or closing the pipe fails.
Alex Vakulenko26d26232014-12-10 12:52:31 -0800170 if (!base::WriteFileDescriptor(stdin_fd, stdin->c_str(),
171 stdin->length()) ||
David Pursell300498a2014-11-03 15:47:36 -0800172 IGNORE_EINTR(close(stdin_fd)) < 0) {
173 process->Kill(SIGKILL, 0);
Eric Carusocc7106c2017-04-27 14:22:42 -0700174 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInputErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800175 }
176 result = process->Wait();
177 } else {
Eric Carusocc7106c2017-04-27 14:22:42 -0700178 DEBUGD_ADD_ERROR(error, kDBusErrorString, kStartErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800179 }
180 } else {
181 result = process->Run();
182 }
183
Eric Carusocc7106c2017-04-27 14:22:42 -0700184 if (stdout)
David Pursell300498a2014-11-03 15:47:36 -0800185 process->GetOutput(stdout);
Eric Carusocc7106c2017-04-27 14:22:42 -0700186
187 if (stderr)
David Pursell300498a2014-11-03 15:47:36 -0800188 process->GetError(stderr);
Eric Carusocc7106c2017-04-27 14:22:42 -0700189
David Pursell300498a2014-11-03 15:47:36 -0800190 return result;
191}
192
Ben Chana0011d82014-05-13 00:19:29 -0700193} // namespace debugd