blob: 36d3e657fc23609ac40ec0a3cc46e7b3793b63f6 [file] [log] [blame]
Ben Chan6f391cb2012-03-21 17:38:21 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cros-disks/process.h"
6
Sergei Datsenkocd676b72019-05-10 11:42:05 +10007#include <algorithm>
8#include <array>
9#include <string>
François Degros899487c2019-07-12 11:57:52 +100010#include <utility>
Sergei Datsenkocd676b72019-05-10 11:42:05 +100011
12#include <poll.h>
13
14#include <base/files/file_util.h>
François Degros8c14d382019-09-12 14:50:43 +100015#include <base/posix/eintr_wrapper.h>
Sergei Datsenkocd676b72019-05-10 11:42:05 +100016#include <base/process/kill.h>
17#include <base/strings/string_util.h>
18#include <base/strings/string_split.h>
19#include <base/time/time.h>
20
François Degros899487c2019-07-12 11:57:52 +100021#include "cros-disks/quote.h"
22
Ben Chan6f391cb2012-03-21 17:38:21 -070023namespace cros_disks {
Sergei Datsenkocd676b72019-05-10 11:42:05 +100024namespace {
25
26enum class ReadResult {
27 kSuccess,
28 kWouldBlock,
29 kFailure,
30};
31
32ReadResult ReadFD(int fd, std::string* data) {
33 const size_t kMaxSize = 4096;
34 char buffer[kMaxSize];
35 ssize_t bytes_read = HANDLE_EINTR(read(fd, buffer, kMaxSize));
36
37 if (bytes_read < 0) {
38 if (errno == EAGAIN || errno == EWOULDBLOCK) {
39 data->clear();
40 return ReadResult::kWouldBlock;
41 }
42 PLOG(ERROR) << "Read failed.";
43 return ReadResult::kFailure;
44 }
45
46 data->assign(buffer, bytes_read);
47 return ReadResult::kSuccess;
48}
49
50// Interleaves streams.
51class StreamMerger {
52 public:
François Degrosd5d26882019-09-13 11:23:51 +100053 explicit StreamMerger(std::vector<std::string>* output) : output_(output) {}
Sergei Datsenkocd676b72019-05-10 11:42:05 +100054
55 ~StreamMerger() {
François Degrosd5d26882019-09-13 11:23:51 +100056 for (size_t i = 0; i < kStreamCount; ++i) {
57 const std::string& remaining = remaining_[i];
58 if (!remaining.empty())
59 output_->push_back(base::JoinString({kTags[i], ": ", remaining}, ""));
Sergei Datsenkocd676b72019-05-10 11:42:05 +100060 }
61 }
62
François Degrosd5d26882019-09-13 11:23:51 +100063 void Append(const size_t stream, const base::StringPiece data) {
64 DCHECK_LT(stream, kStreamCount);
65
Sergei Datsenkocd676b72019-05-10 11:42:05 +100066 if (data.empty()) {
67 return;
68 }
69
François Degrosd5d26882019-09-13 11:23:51 +100070 std::string& remaining = remaining_[stream];
71 const base::StringPiece tag = kTags[stream];
Sergei Datsenkocd676b72019-05-10 11:42:05 +100072
François Degrosd5d26882019-09-13 11:23:51 +100073 std::vector<base::StringPiece> lines = base::SplitStringPiece(
Sergei Datsenkocd676b72019-05-10 11:42:05 +100074 data, "\n", base::WhitespaceHandling::KEEP_WHITESPACE,
75 base::SplitResult::SPLIT_WANT_ALL);
François Degrosd5d26882019-09-13 11:23:51 +100076 const base::StringPiece last_line = lines.back();
Sergei Datsenkocd676b72019-05-10 11:42:05 +100077 lines.pop_back();
78
François Degrosd5d26882019-09-13 11:23:51 +100079 for (const base::StringPiece line : lines) {
80 output_->push_back(base::JoinString({tag, ": ", remaining, line}, ""));
Sergei Datsenkocd676b72019-05-10 11:42:05 +100081 remaining.clear();
82 }
François Degrosd5d26882019-09-13 11:23:51 +100083
84 remaining = last_line.as_string();
Sergei Datsenkocd676b72019-05-10 11:42:05 +100085 }
86
87 private:
François Degrosd5d26882019-09-13 11:23:51 +100088 // Number of streams to interleave.
89 static const size_t kStreamCount = 2;
90 static const base::StringPiece kTags[kStreamCount];
91 std::vector<std::string>* const output_;
92 std::string remaining_[kStreamCount];
Sergei Datsenkocd676b72019-05-10 11:42:05 +100093
94 DISALLOW_COPY_AND_ASSIGN(StreamMerger);
95};
96
François Degrosd5d26882019-09-13 11:23:51 +100097const size_t StreamMerger::kStreamCount;
98const base::StringPiece StreamMerger::kTags[kStreamCount] = {"OUT", "ERR"};
99
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000100} // namespace
101
Ben Chanacac3952012-04-24 22:50:01 -0700102// static
103const pid_t Process::kInvalidProcessId = -1;
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000104const int Process::kInvalidFD = base::ScopedFD::traits_type::InvalidValue();
Ben Chanacac3952012-04-24 22:50:01 -0700105
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000106Process::Process() = default;
Ben Chan6f391cb2012-03-21 17:38:21 -0700107
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000108Process::~Process() = default;
Ben Chan6f391cb2012-03-21 17:38:21 -0700109
Ben Chan213c6d92019-04-10 16:21:52 -0700110void Process::AddArgument(const std::string& argument) {
Ben Chan6f391cb2012-03-21 17:38:21 -0700111 arguments_.push_back(argument);
112}
113
François Degros5593b8c2019-07-25 12:27:42 +1000114char* const* Process::GetArguments() {
Ben Chan5e3ca672014-08-25 15:53:58 -0700115 if (arguments_array_.empty())
Ben Chan6f391cb2012-03-21 17:38:21 -0700116 BuildArgumentsArray();
117
Ben Chan5e3ca672014-08-25 15:53:58 -0700118 return arguments_array_.data();
Ben Chan6f391cb2012-03-21 17:38:21 -0700119}
120
121bool Process::BuildArgumentsArray() {
122 // The following code creates a writable copy of argument strings.
123 size_t num_arguments = arguments_.size();
124 if (num_arguments == 0)
125 return false;
126
127 size_t arguments_buffer_size = 0;
Ben Chan6057fe62016-12-02 10:08:59 -0800128 for (const auto& argument : arguments_) {
129 arguments_buffer_size += argument.size() + 1;
Ben Chan6f391cb2012-03-21 17:38:21 -0700130 }
131
Ben Chan5e3ca672014-08-25 15:53:58 -0700132 arguments_buffer_.resize(arguments_buffer_size);
133 arguments_array_.resize(num_arguments + 1);
Ben Chan6f391cb2012-03-21 17:38:21 -0700134
Ben Chan6057fe62016-12-02 10:08:59 -0800135 char** array_pointer = arguments_array_.data();
Ben Chan5e3ca672014-08-25 15:53:58 -0700136 char* buffer_pointer = arguments_buffer_.data();
Ben Chan6057fe62016-12-02 10:08:59 -0800137 for (const auto& argument : arguments_) {
138 *array_pointer = buffer_pointer;
139 size_t argument_size = argument.size();
140 argument.copy(buffer_pointer, argument_size);
Ben Chan6f391cb2012-03-21 17:38:21 -0700141 buffer_pointer[argument_size] = '\0';
142 buffer_pointer += argument_size + 1;
Ben Chan6057fe62016-12-02 10:08:59 -0800143 ++array_pointer;
Ben Chan6f391cb2012-03-21 17:38:21 -0700144 }
Ben Chan6057fe62016-12-02 10:08:59 -0800145 *array_pointer = nullptr;
Ben Chan6f391cb2012-03-21 17:38:21 -0700146 return true;
147}
148
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000149bool Process::Start() {
150 CHECK_EQ(kInvalidProcessId, pid_);
151 CHECK(!finished_);
François Degros5593b8c2019-07-25 12:27:42 +1000152 CHECK(!arguments_.empty()) << "No arguments provided";
François Degros899487c2019-07-12 11:57:52 +1000153 LOG(INFO) << "Starting process " << quote(arguments_);
François Degros5593b8c2019-07-25 12:27:42 +1000154 pid_ = StartImpl(&in_fd_, &out_fd_, &err_fd_);
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000155 return pid_ != kInvalidProcessId;
156}
157
158int Process::Wait() {
159 if (finished_) {
160 return status_;
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100161 }
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000162
163 CHECK_NE(kInvalidProcessId, pid_);
164 status_ = WaitImpl();
165
166 finished_ = true;
167 pid_ = kInvalidProcessId;
168 return status_;
169}
170
171bool Process::IsFinished() {
172 if (!finished_) {
173 CHECK_NE(kInvalidProcessId, pid_);
174 finished_ = WaitNonBlockingImpl(&status_);
175 }
176 return finished_;
177}
178
179int Process::Run(std::vector<std::string>* output) {
180 if (!Start()) {
181 return -1;
182 }
183
184 if (output) {
185 Communicate(output);
186 } else {
187 in_fd_.reset();
188 out_fd_.reset();
189 err_fd_.reset();
190 }
191
François Degros899487c2019-07-12 11:57:52 +1000192 const int result = Wait();
193
194 LOG(INFO) << "Process finished with return code " << result;
195 if (LOG_IS_ON(INFO) && output && !output->empty()) {
196 LOG(INFO) << "Process outputted " << output->size() << " lines:";
197 for (const std::string& line : *output) {
François Degros16ad1ae2019-07-17 16:02:39 +1000198 LOG(INFO) << line;
François Degros899487c2019-07-12 11:57:52 +1000199 }
200 }
201
202 return result;
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000203}
204
205void Process::Communicate(std::vector<std::string>* output) {
206 // We are not going to write there.
207 in_fd_.reset();
208 // No FD leaves this function alive!
209 base::ScopedFD out_fd = std::move(out_fd_);
210 base::ScopedFD err_fd = std::move(err_fd_);
211
212 if (out_fd.is_valid()) {
213 CHECK(base::SetNonBlocking(out_fd.get()));
214 }
215 if (err_fd.is_valid()) {
216 CHECK(base::SetNonBlocking(err_fd.get()));
217 }
218
219 std::string data;
François Degrosd5d26882019-09-13 11:23:51 +1000220 StreamMerger merger(output);
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000221 std::array<struct pollfd, 2> fds;
222 fds[0] = {out_fd.get(), POLLIN, 0};
223 fds[1] = {err_fd.get(), POLLIN, 0};
224
225 while (!IsFinished()) {
226 size_t still_open = 0;
227 for (const auto& f : fds) {
228 still_open += f.fd != kInvalidFD;
229 }
230 if (still_open == 0) {
231 // No comms expected anymore.
232 break;
233 }
234
François Degros8c14d382019-09-12 14:50:43 +1000235 const int ret =
236 HANDLE_EINTR(poll(fds.data(), fds.size(), 10 /* milliseconds */));
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000237 if (ret == -1) {
238 PLOG(ERROR) << "poll() failed";
239 break;
240 }
241
242 if (ret) {
243 for (size_t i = 0; i < fds.size(); ++i) {
244 auto& f = fds[i];
245 if (f.revents) {
246 if (ReadFD(f.fd, &data) == ReadResult::kFailure) {
247 // Failure.
248 f.fd = kInvalidFD;
249 } else {
250 merger.Append(i, data);
251 }
252 }
253 }
254 }
255 }
256
257 Wait();
258
259 // Final read after process exited.
260 for (size_t i = 0; i < fds.size(); ++i) {
261 auto& f = fds[i];
262 if (f.fd != kInvalidFD) {
263 if (ReadFD(f.fd, &data) == ReadResult::kFailure) {
264 // Failure.
265 f.fd = kInvalidFD;
266 } else {
267 merger.Append(i, data);
268 }
269 }
270 }
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100271}
272
Ben Chan6f391cb2012-03-21 17:38:21 -0700273} // namespace cros_disks