blob: 0648e215764b5b33e9f555c32a5b504fcf1326a1 [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()
29 : separate_stderr_(false), use_minijail_(true) {
30}
Ben Chan78f89532014-08-29 09:35:09 -070031
Elly Jones1c4c3a12011-12-20 15:01:59 -050032ProcessWithOutput::~ProcessWithOutput() {
David Pursell300498a2014-11-03 15:47:36 -080033 outfile_.reset();
34 errfile_.reset();
Ben Chan78f89532014-08-29 09:35:09 -070035
Elly Jones1c4c3a12011-12-20 15:01:59 -050036 if (!outfile_path_.empty())
Ben Chan9953a592014-02-05 23:32:00 -080037 base::DeleteFile(outfile_path_, false); // not recursive
David Pursell300498a2014-11-03 15:47:36 -080038 if (!errfile_path_.empty())
39 base::DeleteFile(errfile_path_, false);
Elly Jones1c4c3a12011-12-20 15:01:59 -050040}
41
42bool ProcessWithOutput::Init() {
Paul Moy970e1122020-01-14 16:17:18 -070043 return Init({});
44}
45
46bool ProcessWithOutput::Init(
47 const std::vector<std::string>& minijail_extra_args) {
David Pursell300498a2014-11-03 15:47:36 -080048 if (use_minijail_) {
Paul Moy970e1122020-01-14 16:17:18 -070049 if (!SandboxedProcess::Init(minijail_extra_args))
David Pursell300498a2014-11-03 15:47:36 -080050 return false;
51 }
Ben Chan78f89532014-08-29 09:35:09 -070052
David Pursell300498a2014-11-03 15:47:36 -080053 outfile_.reset(base::CreateAndOpenTemporaryFile(&outfile_path_));
54 if (!outfile_.get()) {
Elly Jones1c4c3a12011-12-20 15:01:59 -050055 return false;
David Pursell300498a2014-11-03 15:47:36 -080056 }
57 if (separate_stderr_) {
58 errfile_.reset(base::CreateAndOpenTemporaryFile(&errfile_path_));
59 if (!errfile_.get()) {
60 return false;
61 }
62 }
Ben Chan78f89532014-08-29 09:35:09 -070063
Elly Jones1c4c3a12011-12-20 15:01:59 -050064 // We can't just RedirectOutput to the file we just created, since
65 // RedirectOutput uses O_CREAT | O_EXCL to open the target file (i.e., it'll
66 // fail if the file already exists). We can't CreateTemporaryFile() and then
67 // use that filename, since we'd have to remove it before using
68 // RedirectOutput, which exposes us to a /tmp race. Instead, bind outfile_'s
69 // fd to the subprocess's stdout and stderr.
David Pursell300498a2014-11-03 15:47:36 -080070 BindFd(fileno(outfile_.get()), STDOUT_FILENO);
71 BindFd(fileno(separate_stderr_ ? errfile_.get() : outfile_.get()),
72 STDERR_FILENO);
Elly Jones1c4c3a12011-12-20 15:01:59 -050073 return true;
74}
75
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080076bool ProcessWithOutput::GetOutputLines(std::vector<std::string>* output) const {
Elly Jones1c4c3a12011-12-20 15:01:59 -050077 std::string contents;
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080078 if (!GetOutput(&contents))
Elly Jones1c4c3a12011-12-20 15:01:59 -050079 return false;
Ben Chan78f89532014-08-29 09:35:09 -070080
Kevin Cernekee40dcb732018-03-20 15:08:51 -070081 // If the file contains "a\nb\n", base::SplitString() will return a vector
82 // {"a", "b", ""} because it treats "\n" as a delimiter, not an EOL
83 // character. Removing the final "\n" fixes this.
84 if (base::EndsWith(contents, "\n", base::CompareCase::SENSITIVE)) {
85 contents.pop_back();
86 }
87
Alex Vakulenkoe50371c2016-01-20 16:06:19 -080088 *output = base::SplitString(contents, "\n", base::KEEP_WHITESPACE,
89 base::SPLIT_WANT_ALL);
Elly Jones1c4c3a12011-12-20 15:01:59 -050090 return true;
91}
92
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080093bool ProcessWithOutput::GetOutput(std::string* output) const {
Ben Chan9953a592014-02-05 23:32:00 -080094 return base::ReadFileToString(outfile_path_, output);
Elly Jones1c4c3a12011-12-20 15:01:59 -050095}
96
David Pursell300498a2014-11-03 15:47:36 -080097bool ProcessWithOutput::GetError(std::string* error) {
98 return base::ReadFileToString(errfile_path_, error);
99}
100
101int ProcessWithOutput::RunProcess(const std::string& command,
102 const ArgList& arguments,
103 bool requires_root,
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800104 bool disable_sandbox,
David Pursell300498a2014-11-03 15:47:36 -0800105 const std::string* stdin,
106 std::string* stdout,
107 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700108 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800109 ProcessWithOutput process;
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800110 if (disable_sandbox) {
111 process.DisableSandbox();
112 } else if (requires_root) {
David Pursell300498a2014-11-03 15:47:36 -0800113 process.SandboxAs("root", "root");
114 }
115 return DoRunProcess(
116 command, arguments, stdin, stdout, stderr, error, &process);
117}
118
119int ProcessWithOutput::RunHelper(const std::string& helper,
120 const ArgList& arguments,
121 bool requires_root,
122 const std::string* stdin,
123 std::string* stdout,
124 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700125 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800126 std::string helper_path;
Hardik Goyalb09d6b02019-08-13 16:15:50 -0700127 if (!GetHelperPath(helper, &helper_path)) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700128 DEBUGD_ADD_ERROR(error, kDBusErrorString, kPathLengthErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800129 return kRunError;
130 }
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800131 return RunProcess(helper_path, arguments, requires_root,
132 false /* disable_sandbox */, stdin, stdout, stderr, error);
David Pursell300498a2014-11-03 15:47:36 -0800133}
134
135int ProcessWithOutput::RunProcessFromHelper(const std::string& command,
136 const ArgList& arguments,
137 const std::string* stdin,
138 std::string* stdout,
139 std::string* stderr) {
140 ProcessWithOutput process;
141 process.set_use_minijail(false);
142 process.SetSearchPath(true);
143 return DoRunProcess(
144 command, arguments, stdin, stdout, stderr, nullptr, &process);
145}
146
147int ProcessWithOutput::DoRunProcess(const std::string& command,
148 const ArgList& arguments,
149 const std::string* stdin,
150 std::string* stdout,
151 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700152 brillo::ErrorPtr* error,
David Pursell300498a2014-11-03 15:47:36 -0800153 ProcessWithOutput* process) {
154 process->set_separate_stderr(true);
155 if (!process->Init()) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700156 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInitErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800157 return kRunError;
158 }
159
160 process->AddArg(command);
161 for (const auto& argument : arguments) {
162 process->AddArg(argument);
163 }
164
165 int result = kRunError;
166 if (stdin) {
167 process->RedirectUsingPipe(STDIN_FILENO, true);
168 if (process->Start()) {
169 int stdin_fd = process->GetPipe(STDIN_FILENO);
170 // Kill the process if writing to or closing the pipe fails.
Alex Vakulenko26d26232014-12-10 12:52:31 -0800171 if (!base::WriteFileDescriptor(stdin_fd, stdin->c_str(),
172 stdin->length()) ||
David Pursell300498a2014-11-03 15:47:36 -0800173 IGNORE_EINTR(close(stdin_fd)) < 0) {
174 process->Kill(SIGKILL, 0);
Eric Carusocc7106c2017-04-27 14:22:42 -0700175 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInputErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800176 }
177 result = process->Wait();
178 } else {
Eric Carusocc7106c2017-04-27 14:22:42 -0700179 DEBUGD_ADD_ERROR(error, kDBusErrorString, kStartErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800180 }
181 } else {
182 result = process->Run();
183 }
184
Eric Carusocc7106c2017-04-27 14:22:42 -0700185 if (stdout)
David Pursell300498a2014-11-03 15:47:36 -0800186 process->GetOutput(stdout);
Eric Carusocc7106c2017-04-27 14:22:42 -0700187
188 if (stderr)
David Pursell300498a2014-11-03 15:47:36 -0800189 process->GetError(stderr);
Eric Carusocc7106c2017-04-27 14:22:42 -0700190
David Pursell300498a2014-11-03 15:47:36 -0800191 return result;
192}
193
Ben Chana0011d82014-05-13 00:19:29 -0700194} // namespace debugd