blob: 4ec0dad64022ce82c2ee92f0a6e2660871615cc2 [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"
14
Elly Jones1c4c3a12011-12-20 15:01:59 -050015namespace debugd {
16
David Pursell300498a2014-11-03 15:47:36 -080017namespace {
18
19const char kDBusErrorString[] = "org.chromium.debugd.error.RunProcess";
20const char kInitErrorString[] = "Process initialization failure.";
21const char kStartErrorString[] = "Process start failure.";
22const char kInputErrorString[] = "Process input write failure.";
23const char kPathLengthErrorString[] = "Path length is too long.";
24
David Pursell300498a2014-11-03 15:47:36 -080025} // namespace
26
27ProcessWithOutput::ProcessWithOutput()
28 : separate_stderr_(false), use_minijail_(true) {
29}
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() {
David Pursell300498a2014-11-03 15:47:36 -080042 if (use_minijail_) {
43 if (!SandboxedProcess::Init())
44 return false;
45 }
Ben Chan78f89532014-08-29 09:35:09 -070046
David Pursell300498a2014-11-03 15:47:36 -080047 outfile_.reset(base::CreateAndOpenTemporaryFile(&outfile_path_));
48 if (!outfile_.get()) {
Elly Jones1c4c3a12011-12-20 15:01:59 -050049 return false;
David Pursell300498a2014-11-03 15:47:36 -080050 }
51 if (separate_stderr_) {
52 errfile_.reset(base::CreateAndOpenTemporaryFile(&errfile_path_));
53 if (!errfile_.get()) {
54 return false;
55 }
56 }
Ben Chan78f89532014-08-29 09:35:09 -070057
Elly Jones1c4c3a12011-12-20 15:01:59 -050058 // We can't just RedirectOutput to the file we just created, since
59 // RedirectOutput uses O_CREAT | O_EXCL to open the target file (i.e., it'll
60 // fail if the file already exists). We can't CreateTemporaryFile() and then
61 // use that filename, since we'd have to remove it before using
62 // RedirectOutput, which exposes us to a /tmp race. Instead, bind outfile_'s
63 // fd to the subprocess's stdout and stderr.
David Pursell300498a2014-11-03 15:47:36 -080064 BindFd(fileno(outfile_.get()), STDOUT_FILENO);
65 BindFd(fileno(separate_stderr_ ? errfile_.get() : outfile_.get()),
66 STDERR_FILENO);
Elly Jones1c4c3a12011-12-20 15:01:59 -050067 return true;
68}
69
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080070bool ProcessWithOutput::GetOutputLines(std::vector<std::string>* output) const {
Elly Jones1c4c3a12011-12-20 15:01:59 -050071 std::string contents;
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080072 if (!GetOutput(&contents))
Elly Jones1c4c3a12011-12-20 15:01:59 -050073 return false;
Ben Chan78f89532014-08-29 09:35:09 -070074
Kevin Cernekee40dcb732018-03-20 15:08:51 -070075 // If the file contains "a\nb\n", base::SplitString() will return a vector
76 // {"a", "b", ""} because it treats "\n" as a delimiter, not an EOL
77 // character. Removing the final "\n" fixes this.
78 if (base::EndsWith(contents, "\n", base::CompareCase::SENSITIVE)) {
79 contents.pop_back();
80 }
81
Alex Vakulenkoe50371c2016-01-20 16:06:19 -080082 *output = base::SplitString(contents, "\n", base::KEEP_WHITESPACE,
83 base::SPLIT_WANT_ALL);
Elly Jones1c4c3a12011-12-20 15:01:59 -050084 return true;
85}
86
Wei-Cheng Xiao9076cf52018-10-08 14:33:42 +080087bool ProcessWithOutput::GetOutput(std::string* output) const {
Ben Chan9953a592014-02-05 23:32:00 -080088 return base::ReadFileToString(outfile_path_, output);
Elly Jones1c4c3a12011-12-20 15:01:59 -050089}
90
David Pursell300498a2014-11-03 15:47:36 -080091bool ProcessWithOutput::GetError(std::string* error) {
92 return base::ReadFileToString(errfile_path_, error);
93}
94
95int ProcessWithOutput::RunProcess(const std::string& command,
96 const ArgList& arguments,
97 bool requires_root,
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +080098 bool disable_sandbox,
David Pursell300498a2014-11-03 15:47:36 -080099 const std::string* stdin,
100 std::string* stdout,
101 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700102 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800103 ProcessWithOutput process;
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800104 if (disable_sandbox) {
105 process.DisableSandbox();
106 } else if (requires_root) {
David Pursell300498a2014-11-03 15:47:36 -0800107 process.SandboxAs("root", "root");
108 }
109 return DoRunProcess(
110 command, arguments, stdin, stdout, stderr, error, &process);
111}
112
113int ProcessWithOutput::RunHelper(const std::string& helper,
114 const ArgList& arguments,
115 bool requires_root,
116 const std::string* stdin,
117 std::string* stdout,
118 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700119 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -0800120 std::string helper_path;
121 if (!SandboxedProcess::GetHelperPath(helper, &helper_path)) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700122 DEBUGD_ADD_ERROR(error, kDBusErrorString, kPathLengthErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800123 return kRunError;
124 }
Wei-Cheng Xiao88aee2412018-10-15 16:03:45 +0800125 return RunProcess(helper_path, arguments, requires_root,
126 false /* disable_sandbox */, stdin, stdout, stderr, error);
David Pursell300498a2014-11-03 15:47:36 -0800127}
128
129int ProcessWithOutput::RunProcessFromHelper(const std::string& command,
130 const ArgList& arguments,
131 const std::string* stdin,
132 std::string* stdout,
133 std::string* stderr) {
134 ProcessWithOutput process;
135 process.set_use_minijail(false);
136 process.SetSearchPath(true);
137 return DoRunProcess(
138 command, arguments, stdin, stdout, stderr, nullptr, &process);
139}
140
141int ProcessWithOutput::DoRunProcess(const std::string& command,
142 const ArgList& arguments,
143 const std::string* stdin,
144 std::string* stdout,
145 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700146 brillo::ErrorPtr* error,
David Pursell300498a2014-11-03 15:47:36 -0800147 ProcessWithOutput* process) {
148 process->set_separate_stderr(true);
149 if (!process->Init()) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700150 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInitErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800151 return kRunError;
152 }
153
154 process->AddArg(command);
155 for (const auto& argument : arguments) {
156 process->AddArg(argument);
157 }
158
159 int result = kRunError;
160 if (stdin) {
161 process->RedirectUsingPipe(STDIN_FILENO, true);
162 if (process->Start()) {
163 int stdin_fd = process->GetPipe(STDIN_FILENO);
164 // Kill the process if writing to or closing the pipe fails.
Alex Vakulenko26d26232014-12-10 12:52:31 -0800165 if (!base::WriteFileDescriptor(stdin_fd, stdin->c_str(),
166 stdin->length()) ||
David Pursell300498a2014-11-03 15:47:36 -0800167 IGNORE_EINTR(close(stdin_fd)) < 0) {
168 process->Kill(SIGKILL, 0);
Eric Carusocc7106c2017-04-27 14:22:42 -0700169 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInputErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800170 }
171 result = process->Wait();
172 } else {
Eric Carusocc7106c2017-04-27 14:22:42 -0700173 DEBUGD_ADD_ERROR(error, kDBusErrorString, kStartErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800174 }
175 } else {
176 result = process->Run();
177 }
178
Eric Carusocc7106c2017-04-27 14:22:42 -0700179 if (stdout)
David Pursell300498a2014-11-03 15:47:36 -0800180 process->GetOutput(stdout);
Eric Carusocc7106c2017-04-27 14:22:42 -0700181
182 if (stderr)
David Pursell300498a2014-11-03 15:47:36 -0800183 process->GetError(stderr);
Eric Carusocc7106c2017-04-27 14:22:42 -0700184
David Pursell300498a2014-11-03 15:47:36 -0800185 return result;
186}
187
Ben Chana0011d82014-05-13 00:19:29 -0700188} // namespace debugd