blob: a996f52a443ff20a841650f81f476e3381868f33 [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_);
François Degros01564642019-09-13 14:10:17 +1000151 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() {
François Degros01564642019-09-13 14:10:17 +1000159 if (finished()) {
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000160 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();
François Degros01564642019-09-13 14:10:17 +1000165 CHECK(finished());
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000166 pid_ = kInvalidProcessId;
167 return status_;
168}
169
170bool Process::IsFinished() {
François Degros01564642019-09-13 14:10:17 +1000171 if (finished()) {
172 return true;
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000173 }
François Degros01564642019-09-13 14:10:17 +1000174
175 CHECK_NE(kInvalidProcessId, pid_);
176 status_ = WaitNonBlockingImpl();
177 return finished();
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000178}
179
180int Process::Run(std::vector<std::string>* output) {
François Degrosedaefc52019-10-03 12:00:20 +1000181 DCHECK(output);
182
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000183 if (!Start()) {
184 return -1;
185 }
186
François Degrosedaefc52019-10-03 12:00:20 +1000187 Communicate(output);
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000188
François Degros899487c2019-07-12 11:57:52 +1000189 const int result = Wait();
190
191 LOG(INFO) << "Process finished with return code " << result;
François Degrosedaefc52019-10-03 12:00:20 +1000192 if (LOG_IS_ON(INFO) && !output->empty()) {
François Degros899487c2019-07-12 11:57:52 +1000193 LOG(INFO) << "Process outputted " << output->size() << " lines:";
194 for (const std::string& line : *output) {
François Degros16ad1ae2019-07-17 16:02:39 +1000195 LOG(INFO) << line;
François Degros899487c2019-07-12 11:57:52 +1000196 }
197 }
198
199 return result;
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000200}
201
202void Process::Communicate(std::vector<std::string>* output) {
203 // We are not going to write there.
204 in_fd_.reset();
205 // No FD leaves this function alive!
206 base::ScopedFD out_fd = std::move(out_fd_);
207 base::ScopedFD err_fd = std::move(err_fd_);
208
209 if (out_fd.is_valid()) {
210 CHECK(base::SetNonBlocking(out_fd.get()));
211 }
212 if (err_fd.is_valid()) {
213 CHECK(base::SetNonBlocking(err_fd.get()));
214 }
215
216 std::string data;
François Degrosd5d26882019-09-13 11:23:51 +1000217 StreamMerger merger(output);
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000218 std::array<struct pollfd, 2> fds;
219 fds[0] = {out_fd.get(), POLLIN, 0};
220 fds[1] = {err_fd.get(), POLLIN, 0};
221
222 while (!IsFinished()) {
223 size_t still_open = 0;
224 for (const auto& f : fds) {
225 still_open += f.fd != kInvalidFD;
226 }
227 if (still_open == 0) {
228 // No comms expected anymore.
229 break;
230 }
231
François Degros8c14d382019-09-12 14:50:43 +1000232 const int ret =
233 HANDLE_EINTR(poll(fds.data(), fds.size(), 10 /* milliseconds */));
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000234 if (ret == -1) {
235 PLOG(ERROR) << "poll() failed";
236 break;
237 }
238
239 if (ret) {
240 for (size_t i = 0; i < fds.size(); ++i) {
241 auto& f = fds[i];
242 if (f.revents) {
243 if (ReadFD(f.fd, &data) == ReadResult::kFailure) {
244 // Failure.
245 f.fd = kInvalidFD;
246 } else {
247 merger.Append(i, data);
248 }
249 }
250 }
251 }
252 }
253
254 Wait();
255
256 // Final read after process exited.
257 for (size_t i = 0; i < fds.size(); ++i) {
258 auto& f = fds[i];
259 if (f.fd != kInvalidFD) {
260 if (ReadFD(f.fd, &data) == ReadResult::kFailure) {
261 // Failure.
262 f.fd = kInvalidFD;
263 } else {
264 merger.Append(i, data);
265 }
266 }
267 }
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100268}
269
Ben Chan6f391cb2012-03-21 17:38:21 -0700270} // namespace cros_disks