blob: 73919d71033d52c6d8586d5720c0a01ce8b2b572 [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
12namespace debugd {
13
David Pursell300498a2014-11-03 15:47:36 -080014namespace {
15
16const char kDBusErrorString[] = "org.chromium.debugd.error.RunProcess";
17const char kInitErrorString[] = "Process initialization failure.";
18const char kStartErrorString[] = "Process start failure.";
19const char kInputErrorString[] = "Process input write failure.";
20const char kPathLengthErrorString[] = "Path length is too long.";
21
22// Sets the D-Bus error if it's non-NULL.
23void SetError(const char* message, DBus::Error* error) {
24 if (error) {
25 error->set(kDBusErrorString, message);
26 }
27}
28
29} // namespace
30
31ProcessWithOutput::ProcessWithOutput()
32 : separate_stderr_(false), use_minijail_(true) {
33}
Ben Chan78f89532014-08-29 09:35:09 -070034
Elly Jones1c4c3a12011-12-20 15:01:59 -050035ProcessWithOutput::~ProcessWithOutput() {
David Pursell300498a2014-11-03 15:47:36 -080036 outfile_.reset();
37 errfile_.reset();
Ben Chan78f89532014-08-29 09:35:09 -070038
Elly Jones1c4c3a12011-12-20 15:01:59 -050039 if (!outfile_path_.empty())
Ben Chan9953a592014-02-05 23:32:00 -080040 base::DeleteFile(outfile_path_, false); // not recursive
David Pursell300498a2014-11-03 15:47:36 -080041 if (!errfile_path_.empty())
42 base::DeleteFile(errfile_path_, false);
Elly Jones1c4c3a12011-12-20 15:01:59 -050043}
44
45bool ProcessWithOutput::Init() {
David Pursell300498a2014-11-03 15:47:36 -080046 if (use_minijail_) {
47 if (!SandboxedProcess::Init())
48 return false;
49 }
Ben Chan78f89532014-08-29 09:35:09 -070050
David Pursell300498a2014-11-03 15:47:36 -080051 outfile_.reset(base::CreateAndOpenTemporaryFile(&outfile_path_));
52 if (!outfile_.get()) {
Elly Jones1c4c3a12011-12-20 15:01:59 -050053 return false;
David Pursell300498a2014-11-03 15:47:36 -080054 }
55 if (separate_stderr_) {
56 errfile_.reset(base::CreateAndOpenTemporaryFile(&errfile_path_));
57 if (!errfile_.get()) {
58 return false;
59 }
60 }
Ben Chan78f89532014-08-29 09:35:09 -070061
Elly Jones1c4c3a12011-12-20 15:01:59 -050062 // We can't just RedirectOutput to the file we just created, since
63 // RedirectOutput uses O_CREAT | O_EXCL to open the target file (i.e., it'll
64 // fail if the file already exists). We can't CreateTemporaryFile() and then
65 // use that filename, since we'd have to remove it before using
66 // RedirectOutput, which exposes us to a /tmp race. Instead, bind outfile_'s
67 // fd to the subprocess's stdout and stderr.
David Pursell300498a2014-11-03 15:47:36 -080068 BindFd(fileno(outfile_.get()), STDOUT_FILENO);
69 BindFd(fileno(separate_stderr_ ? errfile_.get() : outfile_.get()),
70 STDERR_FILENO);
Elly Jones1c4c3a12011-12-20 15:01:59 -050071 return true;
72}
73
74bool ProcessWithOutput::GetOutputLines(std::vector<std::string>* output) {
75 std::string contents;
Ben Chan9953a592014-02-05 23:32:00 -080076 if (!base::ReadFileToString(outfile_path_, &contents))
Elly Jones1c4c3a12011-12-20 15:01:59 -050077 return false;
Ben Chan78f89532014-08-29 09:35:09 -070078
Elly Jones1c4c3a12011-12-20 15:01:59 -050079 base::SplitString(contents, '\n', output);
80 return true;
81}
82
83bool ProcessWithOutput::GetOutput(std::string* output) {
Ben Chan9953a592014-02-05 23:32:00 -080084 return base::ReadFileToString(outfile_path_, output);
Elly Jones1c4c3a12011-12-20 15:01:59 -050085}
86
David Pursell300498a2014-11-03 15:47:36 -080087bool ProcessWithOutput::GetError(std::string* error) {
88 return base::ReadFileToString(errfile_path_, error);
89}
90
91int ProcessWithOutput::RunProcess(const std::string& command,
92 const ArgList& arguments,
93 bool requires_root,
94 const std::string* stdin,
95 std::string* stdout,
96 std::string* stderr,
97 DBus::Error* error) {
98 ProcessWithOutput process;
99 if (requires_root) {
100 process.SandboxAs("root", "root");
101 }
102 return DoRunProcess(
103 command, arguments, stdin, stdout, stderr, error, &process);
104}
105
106int ProcessWithOutput::RunHelper(const std::string& helper,
107 const ArgList& arguments,
108 bool requires_root,
109 const std::string* stdin,
110 std::string* stdout,
111 std::string* stderr,
112 DBus::Error* error) {
113 std::string helper_path;
114 if (!SandboxedProcess::GetHelperPath(helper, &helper_path)) {
115 SetError(kPathLengthErrorString, error);
116 return kRunError;
117 }
118 return RunProcess(
119 helper_path, arguments, requires_root, stdin, stdout, stderr, error);
120}
121
122int ProcessWithOutput::RunProcessFromHelper(const std::string& command,
123 const ArgList& arguments,
124 const std::string* stdin,
125 std::string* stdout,
126 std::string* stderr) {
127 ProcessWithOutput process;
128 process.set_use_minijail(false);
129 process.SetSearchPath(true);
130 return DoRunProcess(
131 command, arguments, stdin, stdout, stderr, nullptr, &process);
132}
133
134int ProcessWithOutput::DoRunProcess(const std::string& command,
135 const ArgList& arguments,
136 const std::string* stdin,
137 std::string* stdout,
138 std::string* stderr,
139 DBus::Error* error,
140 ProcessWithOutput* process) {
141 process->set_separate_stderr(true);
142 if (!process->Init()) {
143 SetError(kInitErrorString, error);
144 return kRunError;
145 }
146
147 process->AddArg(command);
148 for (const auto& argument : arguments) {
149 process->AddArg(argument);
150 }
151
152 int result = kRunError;
153 if (stdin) {
154 process->RedirectUsingPipe(STDIN_FILENO, true);
155 if (process->Start()) {
156 int stdin_fd = process->GetPipe(STDIN_FILENO);
157 // Kill the process if writing to or closing the pipe fails.
158 if (base::WriteFileDescriptor(stdin_fd, stdin->c_str(),
159 stdin->length()) < 0 ||
160 IGNORE_EINTR(close(stdin_fd)) < 0) {
161 process->Kill(SIGKILL, 0);
162 SetError(kInputErrorString, error);
163 }
164 result = process->Wait();
165 } else {
166 SetError(kStartErrorString, error);
167 }
168 } else {
169 result = process->Run();
170 }
171
172 if (stdout) {
173 process->GetOutput(stdout);
174 }
175 if (stderr) {
176 process->GetError(stderr);
177 }
178 return result;
179}
180
Ben Chana0011d82014-05-13 00:19:29 -0700181} // namespace debugd