blob: 3309ca2f017f22db8a7cb735652b432e0d4664af [file] [log] [blame]
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jorge E. Moreira2a777f62018-06-13 17:28:10 -070017#include "common/libs/utils/subprocess.h"
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070018
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -070019#ifdef __linux__
20#include <sys/prctl.h>
21#endif
22
Steve Kim60b776d2023-06-16 11:01:18 -070023#include <errno.h>
A. Cody Schuffelenffd01ef2022-03-24 15:17:05 -070024#include <fcntl.h>
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -070025#include <signal.h>
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070026#include <stdlib.h>
A. Cody Schuffelenb080c5a2023-08-22 21:42:45 -070027#include <string.h>
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070028#include <sys/types.h>
29#include <sys/wait.h>
30#include <unistd.h>
31
A. Cody Schuffelenffd01ef2022-03-24 15:17:05 -070032#include <cerrno>
33#include <cstring>
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080034#include <map>
A. Cody Schuffelen04d182d2022-07-29 17:49:42 -070035#include <optional>
A. Cody Schuffelenffd01ef2022-03-24 15:17:05 -070036#include <ostream>
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080037#include <set>
A. Cody Schuffelenffd01ef2022-03-24 15:17:05 -070038#include <sstream>
39#include <string>
Cody Schuffelen7e35fb32019-10-03 16:28:35 -070040#include <thread>
A. Cody Schuffelen04d182d2022-07-29 17:49:42 -070041#include <type_traits>
A. Cody Schuffelenffd01ef2022-03-24 15:17:05 -070042#include <utility>
43#include <vector>
44
45#include <android-base/logging.h>
46#include <android-base/strings.h>
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080047
Cody Schuffelen5133a312019-08-02 17:58:00 -070048#include "common/libs/fs/shared_buf.h"
Jason Macnakd8ff5cc2021-09-21 11:13:14 -070049#include "common/libs/utils/files.h"
Cody Schuffelen5133a312019-08-02 17:58:00 -070050
Daniel Norman1c1fa8c2021-08-11 09:42:45 -070051extern char** environ;
52
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +000053namespace cuttlefish {
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070054namespace {
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -070055
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080056// If a redirected-to file descriptor was already closed, it's possible that
57// some inherited file descriptor duped to this file descriptor and the redirect
58// would override that. This function makes sure that doesn't happen.
59bool validate_redirects(
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +000060 const std::map<Subprocess::StdIOChannel, int>& redirects,
61 const std::map<SharedFD, int>& inherited_fds) {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080062 // Add the redirected IO channels to a set as integers. This allows converting
63 // the enum values into integers instead of the other way around.
64 std::set<int> int_redirects;
Jorge E. Moreira189d8232019-08-30 11:43:05 -070065 for (const auto& entry : redirects) {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080066 int_redirects.insert(static_cast<int>(entry.first));
67 }
Jorge E. Moreira189d8232019-08-30 11:43:05 -070068 for (const auto& entry : inherited_fds) {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080069 auto dupped_fd = entry.second;
70 if (int_redirects.count(dupped_fd)) {
71 LOG(ERROR) << "Requested redirect of fd(" << dupped_fd
72 << ") conflicts with inherited FD.";
73 return false;
74 }
75 }
76 return true;
77}
78
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +000079void do_redirects(const std::map<Subprocess::StdIOChannel, int>& redirects) {
Jorge E. Moreira189d8232019-08-30 11:43:05 -070080 for (const auto& entry : redirects) {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -080081 auto std_channel = static_cast<int>(entry.first);
82 auto fd = entry.second;
83 TEMP_FAILURE_RETRY(dup2(fd, std_channel));
84 }
85}
86
Jorge E. Moreira189d8232019-08-30 11:43:05 -070087std::vector<const char*> ToCharPointers(const std::vector<std::string>& vect) {
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070088 std::vector<const char*> ret = {};
89 for (const auto& str : vect) {
90 ret.push_back(str.c_str());
91 }
92 ret.push_back(NULL);
93 return ret;
94}
95} // namespace
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -070096
Steve Kim1f3fd7a2023-04-27 13:15:35 -070097std::vector<std::string> ArgsToVec(char** argv) {
98 std::vector<std::string> args;
99 for (int i = 0; argv && argv[i]; i++) {
100 args.push_back(argv[i]);
101 }
102 return args;
103}
104
105std::unordered_map<std::string, std::string> EnvpToMap(char** envp) {
106 std::unordered_map<std::string, std::string> env_map;
107 if (!envp) {
108 return env_map;
109 }
110 for (char** e = envp; *e != nullptr; e++) {
111 std::string env_var_val(*e);
112 auto tokens = android::base::Split(env_var_val, "=");
113 if (tokens.size() <= 1) {
114 LOG(WARNING) << "Environment var in unknown format: " << env_var_val;
115 continue;
116 }
117 const auto var = tokens.at(0);
118 tokens.erase(tokens.begin());
119 env_map[var] = android::base::Join(tokens, "=");
120 }
121 return env_map;
122}
123
A. Cody Schuffelena3879a52021-11-03 17:48:16 -0700124SubprocessOptions& SubprocessOptions::Verbose(bool verbose) & {
125 verbose_ = verbose;
126 return *this;
127}
128SubprocessOptions SubprocessOptions::Verbose(bool verbose) && {
129 verbose_ = verbose;
130 return *this;
131}
132
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700133#ifdef __linux__
A. Cody Schuffelena3879a52021-11-03 17:48:16 -0700134SubprocessOptions& SubprocessOptions::ExitWithParent(bool v) & {
135 exit_with_parent_ = v;
136 return *this;
137}
138SubprocessOptions SubprocessOptions::ExitWithParent(bool v) && {
139 exit_with_parent_ = v;
140 return *this;
141}
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700142#endif
A. Cody Schuffelena3879a52021-11-03 17:48:16 -0700143
144SubprocessOptions& SubprocessOptions::InGroup(bool in_group) & {
145 in_group_ = in_group;
146 return *this;
147}
148SubprocessOptions SubprocessOptions::InGroup(bool in_group) && {
149 in_group_ = in_group;
150 return *this;
151}
152
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -0800153Subprocess::Subprocess(Subprocess&& subprocess)
Steve Kim60b776d2023-06-16 11:01:18 -0700154 : pid_(subprocess.pid_.load()),
Jorge E. Moreira1a62e762018-11-05 22:05:57 -0800155 started_(subprocess.started_),
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700156 stopper_(subprocess.stopper_) {
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -0800157 // Make sure the moved object no longer controls this subprocess
158 subprocess.pid_ = -1;
159 subprocess.started_ = false;
160}
161
162Subprocess& Subprocess::operator=(Subprocess&& other) {
Steve Kim60b776d2023-06-16 11:01:18 -0700163 pid_ = other.pid_.load();
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -0800164 started_ = other.started_;
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700165 stopper_ = other.stopper_;
Jorge E. Moreira8e9793e2018-11-05 21:57:26 -0800166
167 other.pid_ = -1;
168 other.started_ = false;
169 return *this;
170}
171
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700172int Subprocess::Wait() {
173 if (pid_ < 0) {
174 LOG(ERROR)
175 << "Attempt to wait on invalid pid(has it been waited on already?): "
176 << pid_;
177 return -1;
178 }
179 int wstatus = 0;
Steve Kim60b776d2023-06-16 11:01:18 -0700180 auto pid = pid_.load(); // Wait will set pid_ to -1 after waiting
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800181 auto wait_ret = waitpid(pid, &wstatus, 0);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700182 if (wait_ret < 0) {
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700183 auto error = errno;
184 LOG(ERROR) << "Error on call to waitpid: " << strerror(error);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700185 return wait_ret;
186 }
187 int retval = 0;
188 if (WIFEXITED(wstatus)) {
Steve Kim60b776d2023-06-16 11:01:18 -0700189 pid_ = -1;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700190 retval = WEXITSTATUS(wstatus);
191 if (retval) {
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800192 LOG(DEBUG) << "Subprocess " << pid
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700193 << " exited with error code: " << retval;
194 }
195 } else if (WIFSIGNALED(wstatus)) {
Steve Kim60b776d2023-06-16 11:01:18 -0700196 pid_ = -1;
A. Cody Schuffelenb080c5a2023-08-22 21:42:45 -0700197 int sig_num = WTERMSIG(wstatus);
198 LOG(ERROR) << "Subprocess " << pid << " was interrupted by a signal '"
199 << strsignal(sig_num) << "' (" << sig_num << ")";
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700200 retval = -1;
201 }
202 return retval;
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700203}
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800204int Subprocess::Wait(siginfo_t* infop, int options) {
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700205 if (pid_ < 0) {
206 LOG(ERROR)
207 << "Attempt to wait on invalid pid(has it been waited on already?): "
208 << pid_;
209 return -1;
210 }
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800211 *infop = {};
212 auto retval = waitid(P_PID, pid_, infop, options);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700213 // We don't want to wait twice for the same process
Steve Kim1f3fd7a2023-04-27 13:15:35 -0700214 bool exited = infop->si_code == CLD_EXITED || infop->si_code == CLD_DUMPED;
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800215 bool reaped = !(options & WNOWAIT);
216 if (exited && reaped) {
217 pid_ = -1;
218 }
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700219 return retval;
220}
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700221
Steve Kim60b776d2023-06-16 11:01:18 -0700222static Result<void> SendSignalImpl(const int signal, const pid_t pid,
223 bool to_group, const bool started) {
224 if (pid == -1) {
225 return CF_ERR(strerror(ESRCH));
226 }
227 CF_EXPECTF(started == true,
228 "The Subprocess object lost the ownership"
229 "of the process {}.",
230 pid);
231 int ret_code = 0;
232 if (to_group) {
233 ret_code = killpg(getpgid(pid), signal);
234 } else {
235 ret_code = kill(pid, signal);
236 }
237 CF_EXPECTF(ret_code == 0, "kill/killpg returns {} with errno: {}", ret_code,
238 strerror(errno));
239 return {};
240}
241
242Result<void> Subprocess::SendSignal(const int signal) {
243 CF_EXPECT(SendSignalImpl(signal, pid_, /* to_group */ false, started_));
244 return {};
245}
246
247Result<void> Subprocess::SendSignalToGroup(const int signal) {
248 CF_EXPECT(SendSignalImpl(signal, pid_, /* to_group */ true, started_));
249 return {};
250}
251
A. Cody Schuffelenc37a5ce2021-07-01 18:09:55 -0700252StopperResult KillSubprocess(Subprocess* subprocess) {
Cody Schuffelen5aa643f2020-03-10 17:03:41 +0000253 auto pid = subprocess->pid();
254 if (pid > 0) {
255 auto pgid = getpgid(pid);
256 if (pgid < 0) {
257 auto error = errno;
258 LOG(WARNING) << "Error obtaining process group id of process with pid="
259 << pid << ": " << strerror(error);
260 // Send the kill signal anyways, because pgid will be -1 it will be sent
261 // to the process and not a (non-existent) group
262 }
263 bool is_group_head = pid == pgid;
A. Cody Schuffelenc37a5ce2021-07-01 18:09:55 -0700264 auto kill_ret = (is_group_head ? killpg : kill)(pid, SIGKILL);
265 if (kill_ret == 0) {
266 return StopperResult::kStopSuccess;
Cody Schuffelen5aa643f2020-03-10 17:03:41 +0000267 }
A. Cody Schuffelenc37a5ce2021-07-01 18:09:55 -0700268 auto kill_cmd = is_group_head ? "killpg(" : "kill(";
269 PLOG(ERROR) << kill_cmd << pid << ", SIGKILL) failed: ";
270 return StopperResult::kStopFailure;
Cody Schuffelen5aa643f2020-03-10 17:03:41 +0000271 }
A. Cody Schuffelenc37a5ce2021-07-01 18:09:55 -0700272 return StopperResult::kStopSuccess;
Jorge E. Moreirac4339ad2019-09-05 13:36:06 -0700273}
Jorge E. Moreirab945eb12019-05-06 17:20:38 -0700274
A. Cody Schuffelen6a0331c2022-05-06 14:46:25 -0700275Command::Command(std::string executable, SubprocessStopper stopper)
Daniel Norman1c1fa8c2021-08-11 09:42:45 -0700276 : subprocess_stopper_(stopper) {
277 for (char** env = environ; *env; env++) {
278 env_.emplace_back(*env);
279 }
A. Cody Schuffelen6a0331c2022-05-06 14:46:25 -0700280 command_.emplace_back(std::move(executable));
Daniel Norman1c1fa8c2021-08-11 09:42:45 -0700281}
282
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700283Command::~Command() {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800284 // Close all inherited file descriptors
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700285 for (const auto& entry : inherited_fds_) {
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700286 close(entry.second);
287 }
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800288 // Close all redirected file descriptors
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700289 for (const auto& entry : redirects_) {
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800290 close(entry.second);
291 }
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700292}
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700293
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700294void Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700295 int fd;
296 if (inherited_fds_.count(shared_fd)) {
297 fd = inherited_fds_[shared_fd];
298 } else {
Cody Schuffelenc33a1dc2019-11-18 18:29:00 -0800299 fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700300 CHECK(fd >= 0) << "Could not acquire a new file descriptor: "
301 << shared_fd->StrError();
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700302 inherited_fds_[shared_fd] = fd;
303 }
304 *stream << fd;
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700305}
306
A. Cody Schuffelene8b0b172022-01-25 15:09:30 -0800307Command& Command::RedirectStdIO(Subprocess::StdIOChannel channel,
308 SharedFD shared_fd) & {
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700309 CHECK(shared_fd->IsOpen());
310 CHECK(redirects_.count(channel) == 0)
311 << "Attempted multiple redirections of fd: " << static_cast<int>(channel);
Cody Schuffelenc33a1dc2019-11-18 18:29:00 -0800312 auto dup_fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700313 CHECK(dup_fd >= 0) << "Could not acquire a new file descriptor: "
314 << shared_fd->StrError();
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800315 redirects_[channel] = dup_fd;
A. Cody Schuffelene8b0b172022-01-25 15:09:30 -0800316 return *this;
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800317}
A. Cody Schuffelene8b0b172022-01-25 15:09:30 -0800318Command Command::RedirectStdIO(Subprocess::StdIOChannel channel,
319 SharedFD shared_fd) && {
320 RedirectStdIO(channel, shared_fd);
321 return std::move(*this);
322}
323Command& Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
324 Subprocess::StdIOChannel parent_channel) & {
Cody Schuffelenc374c8c2019-08-21 18:47:42 -0700325 return RedirectStdIO(subprocess_channel,
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000326 SharedFD::Dup(static_cast<int>(parent_channel)));
Cody Schuffelenc374c8c2019-08-21 18:47:42 -0700327}
A. Cody Schuffelene8b0b172022-01-25 15:09:30 -0800328Command Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
329 Subprocess::StdIOChannel parent_channel) && {
330 RedirectStdIO(subprocess_channel, parent_channel);
331 return std::move(*this);
332}
Jorge E. Moreiraa4dac8b2019-01-25 18:14:51 -0800333
A. Cody Schuffelene01f7362022-05-04 18:39:04 -0700334Command& Command::SetWorkingDirectory(const std::string& path) & {
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700335#ifdef __linux__
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700336 auto fd = SharedFD::Open(path, O_RDONLY | O_PATH | O_DIRECTORY);
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700337#elif defined(__APPLE__)
338 auto fd = SharedFD::Open(path, O_RDONLY | O_DIRECTORY);
339#else
340#error "Unsupported operating system"
341#endif
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700342 CHECK(fd->IsOpen()) << "Could not open \"" << path
343 << "\" dir fd: " << fd->StrError();
344 return SetWorkingDirectory(fd);
345}
A. Cody Schuffelene01f7362022-05-04 18:39:04 -0700346Command Command::SetWorkingDirectory(const std::string& path) && {
347 return std::move(SetWorkingDirectory(path));
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700348}
349Command& Command::SetWorkingDirectory(SharedFD dirfd) & {
350 CHECK(dirfd->IsOpen()) << "Dir fd invalid: " << dirfd->StrError();
A. Cody Schuffelene01f7362022-05-04 18:39:04 -0700351 working_directory_ = std::move(dirfd);
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700352 return *this;
353}
354Command Command::SetWorkingDirectory(SharedFD dirfd) && {
A. Cody Schuffelene01f7362022-05-04 18:39:04 -0700355 return std::move(SetWorkingDirectory(std::move(dirfd)));
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700356}
357
JaeMan Parkebd19fc2023-06-16 14:07:44 +0900358Command& Command::AddPrerequisite(
359 const std::function<Result<void>()>& prerequisite) & {
360 prerequisites_.push_back(prerequisite);
361 return *this;
362}
363
364Command Command::AddPrerequisite(
365 const std::function<Result<void>()>& prerequisite) && {
366 prerequisites_.push_back(prerequisite);
367 return std::move(*this);
368}
369
Cody Schuffelenf98a4322019-12-10 17:36:44 -0800370Subprocess Command::Start(SubprocessOptions options) const {
Cody Schuffelen198402c2019-12-10 15:07:30 -0800371 auto cmd = ToCharPointers(command_);
Cody Schuffelen198402c2019-12-10 15:07:30 -0800372
373 if (!validate_redirects(redirects_, inherited_fds_)) {
374 return Subprocess(-1, {});
375 }
376
377 pid_t pid = fork();
378 if (!pid) {
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700379#ifdef __linux__
Cody Schuffelen198402c2019-12-10 15:07:30 -0800380 if (options.ExitWithParent()) {
381 prctl(PR_SET_PDEATHSIG, SIGHUP); // Die when parent dies
382 }
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700383#endif
Cody Schuffelen198402c2019-12-10 15:07:30 -0800384
385 do_redirects(redirects_);
JaeMan Parkebd19fc2023-06-16 14:07:44 +0900386
387 for (auto& prerequisite : prerequisites_) {
388 auto prerequisiteResult = prerequisite();
389
390 if (!prerequisiteResult.ok()) {
391 LOG(ERROR) << "Failed to check prerequisites: "
A. Cody Schuffelenb580d3c2023-08-28 22:28:29 -0700392 << prerequisiteResult.error().FormatForEnv();
JaeMan Parkebd19fc2023-06-16 14:07:44 +0900393 }
394 }
395
Cody Schuffelen198402c2019-12-10 15:07:30 -0800396 if (options.InGroup()) {
397 // This call should never fail (see SETPGID(2))
398 if (setpgid(0, 0) != 0) {
399 auto error = errno;
400 LOG(ERROR) << "setpgid failed (" << strerror(error) << ")";
401 }
402 }
403 for (const auto& entry : inherited_fds_) {
404 if (fcntl(entry.second, F_SETFD, 0)) {
405 int error_num = errno;
406 LOG(ERROR) << "fcntl failed: " << strerror(error_num);
407 }
408 }
A. Cody Schuffelen2f7cdb42022-04-13 18:14:18 -0700409 if (working_directory_->IsOpen()) {
410 if (SharedFD::Fchdir(working_directory_) != 0) {
411 LOG(ERROR) << "Fchdir failed: " << working_directory_->StrError();
412 }
413 }
Cody Schuffelen198402c2019-12-10 15:07:30 -0800414 int rval;
Daniel Norman1c1fa8c2021-08-11 09:42:45 -0700415 auto envp = ToCharPointers(env_);
A. Cody Schuffelen6a0331c2022-05-06 14:46:25 -0700416 const char* executable = executable_ ? executable_->c_str() : cmd[0];
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700417#ifdef __linux__
A. Cody Schuffelen6a0331c2022-05-06 14:46:25 -0700418 rval = execvpe(executable, const_cast<char* const*>(cmd.data()),
Daniel Norman1c1fa8c2021-08-11 09:42:45 -0700419 const_cast<char* const*>(envp.data()));
A. Cody Schuffelen50ee0532023-06-22 15:51:56 -0700420#elif defined(__APPLE__)
421 rval = execve(executable, const_cast<char* const*>(cmd.data()),
422 const_cast<char* const*>(envp.data()));
423#else
424#error "Unsupported architecture"
425#endif
Cody Schuffelen198402c2019-12-10 15:07:30 -0800426 // No need for an if: if exec worked it wouldn't have returned
A. Cody Schuffelen94b6a632022-09-02 13:36:23 -0700427 LOG(ERROR) << "exec of " << cmd[0] << " with path \"" << executable
428 << "\" failed (" << strerror(errno) << ")";
Cody Schuffelen198402c2019-12-10 15:07:30 -0800429 exit(rval);
430 }
431 if (pid == -1) {
432 LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
433 }
A. Cody Schuffelenf7457742020-06-09 14:10:54 -0700434 if (options.Verbose()) { // "more verbose", and LOG(DEBUG) > LOG(VERBOSE)
435 LOG(DEBUG) << "Started (pid: " << pid << "): " << cmd[0];
436 for (int i = 1; cmd[i]; i++) {
437 LOG(DEBUG) << cmd[i];
438 }
439 } else {
440 LOG(VERBOSE) << "Started (pid: " << pid << "): " << cmd[0];
441 for (int i = 1; cmd[i]; i++) {
442 LOG(VERBOSE) << cmd[i];
Cody Schuffelen198402c2019-12-10 15:07:30 -0800443 }
444 }
A. Cody Schuffelen19988a62021-01-13 18:58:52 -0800445 return Subprocess(pid, subprocess_stopper_);
Jorge E. Moreira189d8232019-08-30 11:43:05 -0700446}
447
Jason Macnakd8ff5cc2021-09-21 11:13:14 -0700448std::string Command::AsBashScript(
449 const std::string& redirected_stdio_path) const {
450 CHECK(inherited_fds_.empty())
451 << "Bash wrapper will not have inheritied file descriptors.";
452 CHECK(redirects_.empty()) << "Bash wrapper will not have redirected stdio.";
453
454 std::string contents =
455 "#!/bin/bash\n\n" + android::base::Join(command_, " \\\n");
456 if (!redirected_stdio_path.empty()) {
457 contents += " &> " + AbsolutePath(redirected_stdio_path);
458 }
459 return contents;
460}
461
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700462// A class that waits for threads to exit in its destructor.
463class ThreadJoiner {
464std::vector<std::thread*> threads_;
465public:
466 ThreadJoiner(const std::vector<std::thread*> threads) : threads_(threads) {}
467 ~ThreadJoiner() {
468 for (auto& thread : threads_) {
469 if (thread->joinable()) {
470 thread->join();
471 }
472 }
473 }
474};
475
Colin Crossb53586e2021-09-13 16:19:15 -0700476int RunWithManagedStdio(Command&& cmd_tmp, const std::string* stdin_str,
477 std::string* stdout_str, std::string* stderr_str,
Cody Schuffelenf98a4322019-12-10 17:36:44 -0800478 SubprocessOptions options) {
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700479 /*
480 * The order of these declarations is necessary for safety. If the function
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000481 * returns at any point, the Command will be destroyed first, closing all
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700482 * of its references to SharedFDs. This will cause the thread internals to fail
483 * their reads or writes. The ThreadJoiner then waits for the threads to
484 * complete, as running the destructor of an active std::thread crashes the
485 * program.
486 *
487 * C++ scoping rules dictate that objects are descoped in reverse order to
488 * construction, so this behavior is predictable.
489 */
490 std::thread stdin_thread, stdout_thread, stderr_thread;
491 ThreadJoiner thread_joiner({&stdin_thread, &stdout_thread, &stderr_thread});
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000492 Command cmd = std::move(cmd_tmp);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700493 bool io_error = false;
Colin Crossb53586e2021-09-13 16:19:15 -0700494 if (stdin_str != nullptr) {
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000495 SharedFD pipe_read, pipe_write;
496 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700497 LOG(ERROR) << "Could not create a pipe to write the stdin of \""
498 << cmd.GetShortName() << "\"";
499 return -1;
500 }
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700501 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read);
Colin Crossb53586e2021-09-13 16:19:15 -0700502 stdin_thread = std::thread([pipe_write, stdin_str, &io_error]() {
503 int written = WriteAll(pipe_write, *stdin_str);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700504 if (written < 0) {
505 io_error = true;
506 LOG(ERROR) << "Error in writing stdin to process";
507 }
508 });
509 }
Colin Crossb53586e2021-09-13 16:19:15 -0700510 if (stdout_str != nullptr) {
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000511 SharedFD pipe_read, pipe_write;
512 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700513 LOG(ERROR) << "Could not create a pipe to read the stdout of \""
514 << cmd.GetShortName() << "\"";
515 return -1;
516 }
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700517 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write);
Colin Crossb53586e2021-09-13 16:19:15 -0700518 stdout_thread = std::thread([pipe_read, stdout_str, &io_error]() {
519 int read = ReadAll(pipe_read, stdout_str);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700520 if (read < 0) {
521 io_error = true;
522 LOG(ERROR) << "Error in reading stdout from process";
523 }
524 });
525 }
Colin Crossb53586e2021-09-13 16:19:15 -0700526 if (stderr_str != nullptr) {
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000527 SharedFD pipe_read, pipe_write;
528 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700529 LOG(ERROR) << "Could not create a pipe to read the stderr of \""
530 << cmd.GetShortName() << "\"";
531 return -1;
532 }
A. Cody Schuffelenc99c6d42021-07-02 17:58:59 -0700533 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write);
Colin Crossb53586e2021-09-13 16:19:15 -0700534 stderr_thread = std::thread([pipe_read, stderr_str, &io_error]() {
535 int read = ReadAll(pipe_read, stderr_str);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700536 if (read < 0) {
537 io_error = true;
538 LOG(ERROR) << "Error in reading stderr from process";
539 }
540 });
541 }
542
Cody Schuffelenf98a4322019-12-10 17:36:44 -0800543 auto subprocess = cmd.Start(options);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700544 if (!subprocess.Started()) {
545 return -1;
546 }
Chih-Hung Hsiehb8027db2020-03-06 15:53:01 -0800547 auto cmd_short_name = cmd.GetShortName();
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700548 {
549 // Force the destructor to run by moving it into a smaller scope.
550 // This is necessary to close the write end of the pipe.
A. Cody Schuffelend4b6b552020-06-23 22:26:02 +0000551 Command forceDelete = std::move(cmd);
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700552 }
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800553
554 int code = subprocess.Wait();
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700555 {
556 auto join_threads = std::move(thread_joiner);
557 }
558 if (io_error) {
Chih-Hung Hsiehb8027db2020-03-06 15:53:01 -0800559 LOG(ERROR) << "IO error communicating with " << cmd_short_name;
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700560 return -1;
561 }
A. Cody Schuffelen658b8b72022-02-25 16:07:31 -0800562 return code;
Cody Schuffelen7e35fb32019-10-03 16:28:35 -0700563}
564
Steve Kim49514282023-05-01 17:38:57 -0700565namespace {
566
567struct ExtraParam {
568 // option for Subprocess::Start()
569 SubprocessOptions subprocess_options;
570 // options for Subprocess::Wait(...)
571 int wait_options;
572 siginfo_t* infop;
573};
574Result<int> ExecuteImpl(const std::vector<std::string>& command,
575 const std::optional<std::vector<std::string>>& envs,
576 const std::optional<ExtraParam>& extra_param) {
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700577 Command cmd(command[0]);
578 for (size_t i = 1; i < command.size(); ++i) {
579 cmd.AddParameter(command[i]);
580 }
Steve Kim49514282023-05-01 17:38:57 -0700581 if (envs) {
582 cmd.SetEnvironment(*envs);
Jorge E. Moreira6ffa0cb2018-10-24 17:19:39 -0700583 }
Steve Kim49514282023-05-01 17:38:57 -0700584 auto subprocess =
585 (!extra_param ? cmd.Start() : cmd.Start(extra_param->subprocess_options));
586 CF_EXPECT(subprocess.Started(), "Subprocess failed to start.");
587
588 if (extra_param) {
589 CF_EXPECT(extra_param->infop != nullptr,
590 "When ExtraParam is given, the infop buffer address "
591 << "must not be nullptr.");
592 return subprocess.Wait(extra_param->infop, extra_param->wait_options);
593 } else {
594 return subprocess.Wait();
595 }
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700596}
Steve Kim49514282023-05-01 17:38:57 -0700597
598} // namespace
599
600int Execute(const std::vector<std::string>& commands,
601 const std::vector<std::string>& envs) {
602 auto result = ExecuteImpl(commands, envs, /* extra_param */ std::nullopt);
603 return (!result.ok() ? -1 : *result);
604}
605
606int Execute(const std::vector<std::string>& commands) {
607 std::vector<std::string> envs;
608 auto result = ExecuteImpl(commands, /* envs */ std::nullopt,
609 /* extra_param */ std::nullopt);
610 return (!result.ok() ? -1 : *result);
611}
612
613Result<siginfo_t> Execute(const std::vector<std::string>& commands,
614 SubprocessOptions subprocess_options,
615 int wait_options) {
616 siginfo_t info;
617 auto ret_code =
618 CF_EXPECT(ExecuteImpl(commands, /* envs */ std::nullopt,
619 ExtraParam{.subprocess_options = subprocess_options,
Steve Kim2bcb6bf2023-11-23 22:19:45 +0000620 .wait_options = wait_options,
621 .infop = &info}));
Steve Kim49514282023-05-01 17:38:57 -0700622 CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
623 return info;
624}
625
626Result<siginfo_t> Execute(const std::vector<std::string>& commands,
627 const std::vector<std::string>& envs,
628 SubprocessOptions subprocess_options,
629 int wait_options) {
630 siginfo_t info;
631 auto ret_code =
632 CF_EXPECT(ExecuteImpl(commands, envs,
633 ExtraParam{.subprocess_options = subprocess_options,
Steve Kim2bcb6bf2023-11-23 22:19:45 +0000634 .wait_options = wait_options,
635 .infop = &info}));
Steve Kim49514282023-05-01 17:38:57 -0700636 CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
637 return info;
Jorge E. Moreira6a9d6292018-06-11 11:52:57 -0700638}
Cody Schuffelen5133a312019-08-02 17:58:00 -0700639
A. Cody Schuffelen51c5e5e2020-06-22 22:43:37 +0000640} // namespace cuttlefish