blob: f9b3c299ee27f0a5b3155f5c721e951c42c908f7 [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>
Elly Jones1c4c3a12011-12-20 15:01:59 -050011
Eric Carusocc7106c2017-04-27 14:22:42 -070012#include "debugd/src/error_utils.h"
13
Elly Jones1c4c3a12011-12-20 15:01:59 -050014namespace debugd {
15
David Pursell300498a2014-11-03 15:47:36 -080016namespace {
17
18const char kDBusErrorString[] = "org.chromium.debugd.error.RunProcess";
19const char kInitErrorString[] = "Process initialization failure.";
20const char kStartErrorString[] = "Process start failure.";
21const char kInputErrorString[] = "Process input write failure.";
22const char kPathLengthErrorString[] = "Path length is too long.";
23
David Pursell300498a2014-11-03 15:47:36 -080024} // namespace
25
26ProcessWithOutput::ProcessWithOutput()
27 : separate_stderr_(false), use_minijail_(true) {
28}
Ben Chan78f89532014-08-29 09:35:09 -070029
Elly Jones1c4c3a12011-12-20 15:01:59 -050030ProcessWithOutput::~ProcessWithOutput() {
David Pursell300498a2014-11-03 15:47:36 -080031 outfile_.reset();
32 errfile_.reset();
Ben Chan78f89532014-08-29 09:35:09 -070033
Elly Jones1c4c3a12011-12-20 15:01:59 -050034 if (!outfile_path_.empty())
Ben Chan9953a592014-02-05 23:32:00 -080035 base::DeleteFile(outfile_path_, false); // not recursive
David Pursell300498a2014-11-03 15:47:36 -080036 if (!errfile_path_.empty())
37 base::DeleteFile(errfile_path_, false);
Elly Jones1c4c3a12011-12-20 15:01:59 -050038}
39
40bool ProcessWithOutput::Init() {
David Pursell300498a2014-11-03 15:47:36 -080041 if (use_minijail_) {
42 if (!SandboxedProcess::Init())
43 return false;
44 }
Ben Chan78f89532014-08-29 09:35:09 -070045
David Pursell300498a2014-11-03 15:47:36 -080046 outfile_.reset(base::CreateAndOpenTemporaryFile(&outfile_path_));
47 if (!outfile_.get()) {
Elly Jones1c4c3a12011-12-20 15:01:59 -050048 return false;
David Pursell300498a2014-11-03 15:47:36 -080049 }
50 if (separate_stderr_) {
51 errfile_.reset(base::CreateAndOpenTemporaryFile(&errfile_path_));
52 if (!errfile_.get()) {
53 return false;
54 }
55 }
Ben Chan78f89532014-08-29 09:35:09 -070056
Elly Jones1c4c3a12011-12-20 15:01:59 -050057 // We can't just RedirectOutput to the file we just created, since
58 // RedirectOutput uses O_CREAT | O_EXCL to open the target file (i.e., it'll
59 // fail if the file already exists). We can't CreateTemporaryFile() and then
60 // use that filename, since we'd have to remove it before using
61 // RedirectOutput, which exposes us to a /tmp race. Instead, bind outfile_'s
62 // fd to the subprocess's stdout and stderr.
David Pursell300498a2014-11-03 15:47:36 -080063 BindFd(fileno(outfile_.get()), STDOUT_FILENO);
64 BindFd(fileno(separate_stderr_ ? errfile_.get() : outfile_.get()),
65 STDERR_FILENO);
Elly Jones1c4c3a12011-12-20 15:01:59 -050066 return true;
67}
68
69bool ProcessWithOutput::GetOutputLines(std::vector<std::string>* output) {
70 std::string contents;
Ben Chan9953a592014-02-05 23:32:00 -080071 if (!base::ReadFileToString(outfile_path_, &contents))
Elly Jones1c4c3a12011-12-20 15:01:59 -050072 return false;
Ben Chan78f89532014-08-29 09:35:09 -070073
Alex Vakulenkoe50371c2016-01-20 16:06:19 -080074 *output = base::SplitString(contents, "\n", base::KEEP_WHITESPACE,
75 base::SPLIT_WANT_ALL);
Elly Jones1c4c3a12011-12-20 15:01:59 -050076 return true;
77}
78
79bool ProcessWithOutput::GetOutput(std::string* output) {
Ben Chan9953a592014-02-05 23:32:00 -080080 return base::ReadFileToString(outfile_path_, output);
Elly Jones1c4c3a12011-12-20 15:01:59 -050081}
82
David Pursell300498a2014-11-03 15:47:36 -080083bool ProcessWithOutput::GetError(std::string* error) {
84 return base::ReadFileToString(errfile_path_, error);
85}
86
87int ProcessWithOutput::RunProcess(const std::string& command,
88 const ArgList& arguments,
89 bool requires_root,
90 const std::string* stdin,
91 std::string* stdout,
92 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -070093 brillo::ErrorPtr* error) {
David Pursell300498a2014-11-03 15:47:36 -080094 ProcessWithOutput process;
95 if (requires_root) {
96 process.SandboxAs("root", "root");
97 }
98 return DoRunProcess(
99 command, arguments, stdin, stdout, stderr, error, &process);
100}
101
102int ProcessWithOutput::RunHelper(const std::string& helper,
103 const ArgList& arguments,
104 bool requires_root,
105 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 std::string helper_path;
110 if (!SandboxedProcess::GetHelperPath(helper, &helper_path)) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700111 DEBUGD_ADD_ERROR(error, kDBusErrorString, kPathLengthErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800112 return kRunError;
113 }
114 return RunProcess(
115 helper_path, arguments, requires_root, stdin, stdout, stderr, error);
116}
117
118int ProcessWithOutput::RunProcessFromHelper(const std::string& command,
119 const ArgList& arguments,
120 const std::string* stdin,
121 std::string* stdout,
122 std::string* stderr) {
123 ProcessWithOutput process;
124 process.set_use_minijail(false);
125 process.SetSearchPath(true);
126 return DoRunProcess(
127 command, arguments, stdin, stdout, stderr, nullptr, &process);
128}
129
130int ProcessWithOutput::DoRunProcess(const std::string& command,
131 const ArgList& arguments,
132 const std::string* stdin,
133 std::string* stdout,
134 std::string* stderr,
Eric Carusocc7106c2017-04-27 14:22:42 -0700135 brillo::ErrorPtr* error,
David Pursell300498a2014-11-03 15:47:36 -0800136 ProcessWithOutput* process) {
137 process->set_separate_stderr(true);
138 if (!process->Init()) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700139 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInitErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800140 return kRunError;
141 }
142
143 process->AddArg(command);
144 for (const auto& argument : arguments) {
145 process->AddArg(argument);
146 }
147
148 int result = kRunError;
149 if (stdin) {
150 process->RedirectUsingPipe(STDIN_FILENO, true);
151 if (process->Start()) {
152 int stdin_fd = process->GetPipe(STDIN_FILENO);
153 // Kill the process if writing to or closing the pipe fails.
Alex Vakulenko26d26232014-12-10 12:52:31 -0800154 if (!base::WriteFileDescriptor(stdin_fd, stdin->c_str(),
155 stdin->length()) ||
David Pursell300498a2014-11-03 15:47:36 -0800156 IGNORE_EINTR(close(stdin_fd)) < 0) {
157 process->Kill(SIGKILL, 0);
Eric Carusocc7106c2017-04-27 14:22:42 -0700158 DEBUGD_ADD_ERROR(error, kDBusErrorString, kInputErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800159 }
160 result = process->Wait();
161 } else {
Eric Carusocc7106c2017-04-27 14:22:42 -0700162 DEBUGD_ADD_ERROR(error, kDBusErrorString, kStartErrorString);
David Pursell300498a2014-11-03 15:47:36 -0800163 }
164 } else {
165 result = process->Run();
166 }
167
Eric Carusocc7106c2017-04-27 14:22:42 -0700168 if (stdout)
David Pursell300498a2014-11-03 15:47:36 -0800169 process->GetOutput(stdout);
Eric Carusocc7106c2017-04-27 14:22:42 -0700170
171 if (stderr)
David Pursell300498a2014-11-03 15:47:36 -0800172 process->GetError(stderr);
Eric Carusocc7106c2017-04-27 14:22:42 -0700173
David Pursell300498a2014-11-03 15:47:36 -0800174 return result;
175}
176
Ben Chana0011d82014-05-13 00:19:29 -0700177} // namespace debugd