1 /*
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
17 #include "common/libs/utils/subprocess.h"
18
19 #ifdef __linux__
20 #include <sys/prctl.h>
21 #endif
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31
32 #include <cerrno>
33 #include <cstring>
34 #include <map>
35 #include <optional>
36 #include <ostream>
37 #include <set>
38 #include <sstream>
39 #include <string>
40 #include <thread>
41 #include <type_traits>
42 #include <utility>
43 #include <vector>
44
45 #include <android-base/logging.h>
46 #include <android-base/strings.h>
47
48 #include "common/libs/fs/shared_buf.h"
49 #include "common/libs/utils/files.h"
50
51 extern char** environ;
52
53 namespace cuttlefish {
54 namespace {
55
56 // 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.
validate_redirects(const std::map<Subprocess::StdIOChannel,int> & redirects,const std::map<SharedFD,int> & inherited_fds)59 bool validate_redirects(
60 const std::map<Subprocess::StdIOChannel, int>& redirects,
61 const std::map<SharedFD, int>& inherited_fds) {
62 // 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;
65 for (const auto& entry : redirects) {
66 int_redirects.insert(static_cast<int>(entry.first));
67 }
68 for (const auto& entry : inherited_fds) {
69 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
do_redirects(const std::map<Subprocess::StdIOChannel,int> & redirects)79 void do_redirects(const std::map<Subprocess::StdIOChannel, int>& redirects) {
80 for (const auto& entry : redirects) {
81 auto std_channel = static_cast<int>(entry.first);
82 auto fd = entry.second;
83 TEMP_FAILURE_RETRY(dup2(fd, std_channel));
84 }
85 }
86
ToCharPointers(const std::vector<std::string> & vect)87 std::vector<const char*> ToCharPointers(const std::vector<std::string>& vect) {
88 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
96
ArgsToVec(char ** argv)97 std::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
EnvpToMap(char ** envp)105 std::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
Verbose(bool verbose)124 SubprocessOptions& SubprocessOptions::Verbose(bool verbose) & {
125 verbose_ = verbose;
126 return *this;
127 }
Verbose(bool verbose)128 SubprocessOptions SubprocessOptions::Verbose(bool verbose) && {
129 verbose_ = verbose;
130 return std::move(*this);
131 }
132
133 #ifdef __linux__
ExitWithParent(bool v)134 SubprocessOptions& SubprocessOptions::ExitWithParent(bool v) & {
135 exit_with_parent_ = v;
136 return *this;
137 }
ExitWithParent(bool v)138 SubprocessOptions SubprocessOptions::ExitWithParent(bool v) && {
139 exit_with_parent_ = v;
140 return std::move(*this);
141 }
142 #endif
143
InGroup(bool in_group)144 SubprocessOptions& SubprocessOptions::InGroup(bool in_group) & {
145 in_group_ = in_group;
146 return *this;
147 }
InGroup(bool in_group)148 SubprocessOptions SubprocessOptions::InGroup(bool in_group) && {
149 in_group_ = in_group;
150 return std::move(*this);
151 }
152
Strace(std::string s)153 SubprocessOptions& SubprocessOptions::Strace(std::string s) & {
154 strace_ = std::move(s);
155 return *this;
156 }
Strace(std::string s)157 SubprocessOptions SubprocessOptions::Strace(std::string s) && {
158 strace_ = std::move(s);
159 return std::move(*this);
160 }
161
Subprocess(Subprocess && subprocess)162 Subprocess::Subprocess(Subprocess&& subprocess)
163 : pid_(subprocess.pid_.load()),
164 started_(subprocess.started_),
165 stopper_(subprocess.stopper_) {
166 // Make sure the moved object no longer controls this subprocess
167 subprocess.pid_ = -1;
168 subprocess.started_ = false;
169 }
170
operator =(Subprocess && other)171 Subprocess& Subprocess::operator=(Subprocess&& other) {
172 pid_ = other.pid_.load();
173 started_ = other.started_;
174 stopper_ = other.stopper_;
175
176 other.pid_ = -1;
177 other.started_ = false;
178 return *this;
179 }
180
Wait()181 int Subprocess::Wait() {
182 if (pid_ < 0) {
183 LOG(ERROR)
184 << "Attempt to wait on invalid pid(has it been waited on already?): "
185 << pid_;
186 return -1;
187 }
188 int wstatus = 0;
189 auto pid = pid_.load(); // Wait will set pid_ to -1 after waiting
190 auto wait_ret = waitpid(pid, &wstatus, 0);
191 if (wait_ret < 0) {
192 auto error = errno;
193 LOG(ERROR) << "Error on call to waitpid: " << strerror(error);
194 return wait_ret;
195 }
196 int retval = 0;
197 if (WIFEXITED(wstatus)) {
198 pid_ = -1;
199 retval = WEXITSTATUS(wstatus);
200 if (retval) {
201 LOG(DEBUG) << "Subprocess " << pid
202 << " exited with error code: " << retval;
203 }
204 } else if (WIFSIGNALED(wstatus)) {
205 pid_ = -1;
206 int sig_num = WTERMSIG(wstatus);
207 LOG(ERROR) << "Subprocess " << pid << " was interrupted by a signal '"
208 << strsignal(sig_num) << "' (" << sig_num << ")";
209 retval = -1;
210 }
211 return retval;
212 }
Wait(siginfo_t * infop,int options)213 int Subprocess::Wait(siginfo_t* infop, int options) {
214 if (pid_ < 0) {
215 LOG(ERROR)
216 << "Attempt to wait on invalid pid(has it been waited on already?): "
217 << pid_;
218 return -1;
219 }
220 *infop = {};
221 auto retval = TEMP_FAILURE_RETRY(waitid(P_PID, pid_, infop, options));
222 // We don't want to wait twice for the same process
223 bool exited = infop->si_code == CLD_EXITED || infop->si_code == CLD_DUMPED;
224 bool reaped = !(options & WNOWAIT);
225 if (exited && reaped) {
226 pid_ = -1;
227 }
228 return retval;
229 }
230
SendSignalImpl(const int signal,const pid_t pid,bool to_group,const bool started)231 static Result<void> SendSignalImpl(const int signal, const pid_t pid,
232 bool to_group, const bool started) {
233 if (pid == -1) {
234 return CF_ERR(strerror(ESRCH));
235 }
236 CF_EXPECTF(started == true,
237 "The Subprocess object lost the ownership"
238 "of the process {}.",
239 pid);
240 int ret_code = 0;
241 if (to_group) {
242 ret_code = killpg(getpgid(pid), signal);
243 } else {
244 ret_code = kill(pid, signal);
245 }
246 CF_EXPECTF(ret_code == 0, "kill/killpg returns {} with errno: {}", ret_code,
247 strerror(errno));
248 return {};
249 }
250
SendSignal(const int signal)251 Result<void> Subprocess::SendSignal(const int signal) {
252 CF_EXPECT(SendSignalImpl(signal, pid_, /* to_group */ false, started_));
253 return {};
254 }
255
SendSignalToGroup(const int signal)256 Result<void> Subprocess::SendSignalToGroup(const int signal) {
257 CF_EXPECT(SendSignalImpl(signal, pid_, /* to_group */ true, started_));
258 return {};
259 }
260
KillSubprocess(Subprocess * subprocess)261 StopperResult KillSubprocess(Subprocess* subprocess) {
262 auto pid = subprocess->pid();
263 if (pid > 0) {
264 auto pgid = getpgid(pid);
265 if (pgid < 0) {
266 auto error = errno;
267 LOG(WARNING) << "Error obtaining process group id of process with pid="
268 << pid << ": " << strerror(error);
269 // Send the kill signal anyways, because pgid will be -1 it will be sent
270 // to the process and not a (non-existent) group
271 }
272 bool is_group_head = pid == pgid;
273 auto kill_ret = (is_group_head ? killpg : kill)(pid, SIGKILL);
274 if (kill_ret == 0) {
275 return StopperResult::kStopSuccess;
276 }
277 auto kill_cmd = is_group_head ? "killpg(" : "kill(";
278 PLOG(ERROR) << kill_cmd << pid << ", SIGKILL) failed: ";
279 return StopperResult::kStopFailure;
280 }
281 return StopperResult::kStopSuccess;
282 }
283
KillSubprocessFallback(std::function<StopperResult ()> nice)284 SubprocessStopper KillSubprocessFallback(std::function<StopperResult()> nice) {
285 return KillSubprocessFallback([nice](Subprocess*) { return nice(); });
286 }
287
KillSubprocessFallback(SubprocessStopper nice_stopper)288 SubprocessStopper KillSubprocessFallback(SubprocessStopper nice_stopper) {
289 return [nice_stopper](Subprocess* process) {
290 auto nice_result = nice_stopper(process);
291 if (nice_result == StopperResult::kStopFailure) {
292 auto harsh_result = KillSubprocess(process);
293 return harsh_result == StopperResult::kStopSuccess
294 ? StopperResult::kStopCrash
295 : harsh_result;
296 }
297 return nice_result;
298 };
299 }
300
Command(std::string executable,SubprocessStopper stopper)301 Command::Command(std::string executable, SubprocessStopper stopper)
302 : subprocess_stopper_(stopper) {
303 for (char** env = environ; *env; env++) {
304 env_.emplace_back(*env);
305 }
306 command_.emplace_back(std::move(executable));
307 }
308
~Command()309 Command::~Command() {
310 // Close all inherited file descriptors
311 for (const auto& entry : inherited_fds_) {
312 close(entry.second);
313 }
314 // Close all redirected file descriptors
315 for (const auto& entry : redirects_) {
316 close(entry.second);
317 }
318 }
319
BuildParameter(std::stringstream * stream,SharedFD shared_fd)320 void Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
321 int fd;
322 if (inherited_fds_.count(shared_fd)) {
323 fd = inherited_fds_[shared_fd];
324 } else {
325 fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
326 CHECK(fd >= 0) << "Could not acquire a new file descriptor: "
327 << shared_fd->StrError();
328 inherited_fds_[shared_fd] = fd;
329 }
330 *stream << fd;
331 }
332
RedirectStdIO(Subprocess::StdIOChannel channel,SharedFD shared_fd)333 Command& Command::RedirectStdIO(Subprocess::StdIOChannel channel,
334 SharedFD shared_fd) & {
335 CHECK(shared_fd->IsOpen());
336 CHECK(redirects_.count(channel) == 0)
337 << "Attempted multiple redirections of fd: " << static_cast<int>(channel);
338 auto dup_fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
339 CHECK(dup_fd >= 0) << "Could not acquire a new file descriptor: "
340 << shared_fd->StrError();
341 redirects_[channel] = dup_fd;
342 return *this;
343 }
RedirectStdIO(Subprocess::StdIOChannel channel,SharedFD shared_fd)344 Command Command::RedirectStdIO(Subprocess::StdIOChannel channel,
345 SharedFD shared_fd) && {
346 RedirectStdIO(channel, shared_fd);
347 return std::move(*this);
348 }
RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,Subprocess::StdIOChannel parent_channel)349 Command& Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
350 Subprocess::StdIOChannel parent_channel) & {
351 return RedirectStdIO(subprocess_channel,
352 SharedFD::Dup(static_cast<int>(parent_channel)));
353 }
RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,Subprocess::StdIOChannel parent_channel)354 Command Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
355 Subprocess::StdIOChannel parent_channel) && {
356 RedirectStdIO(subprocess_channel, parent_channel);
357 return std::move(*this);
358 }
359
SetWorkingDirectory(const std::string & path)360 Command& Command::SetWorkingDirectory(const std::string& path) & {
361 #ifdef __linux__
362 auto fd = SharedFD::Open(path, O_RDONLY | O_PATH | O_DIRECTORY);
363 #elif defined(__APPLE__)
364 auto fd = SharedFD::Open(path, O_RDONLY | O_DIRECTORY);
365 #else
366 #error "Unsupported operating system"
367 #endif
368 CHECK(fd->IsOpen()) << "Could not open \"" << path
369 << "\" dir fd: " << fd->StrError();
370 return SetWorkingDirectory(fd);
371 }
SetWorkingDirectory(const std::string & path)372 Command Command::SetWorkingDirectory(const std::string& path) && {
373 return std::move(SetWorkingDirectory(path));
374 }
SetWorkingDirectory(SharedFD dirfd)375 Command& Command::SetWorkingDirectory(SharedFD dirfd) & {
376 CHECK(dirfd->IsOpen()) << "Dir fd invalid: " << dirfd->StrError();
377 working_directory_ = std::move(dirfd);
378 return *this;
379 }
SetWorkingDirectory(SharedFD dirfd)380 Command Command::SetWorkingDirectory(SharedFD dirfd) && {
381 return std::move(SetWorkingDirectory(std::move(dirfd)));
382 }
383
AddPrerequisite(const std::function<Result<void> ()> & prerequisite)384 Command& Command::AddPrerequisite(
385 const std::function<Result<void>()>& prerequisite) & {
386 prerequisites_.push_back(prerequisite);
387 return *this;
388 }
389
AddPrerequisite(const std::function<Result<void> ()> & prerequisite)390 Command Command::AddPrerequisite(
391 const std::function<Result<void>()>& prerequisite) && {
392 prerequisites_.push_back(prerequisite);
393 return std::move(*this);
394 }
395
Start(SubprocessOptions options) const396 Subprocess Command::Start(SubprocessOptions options) const {
397 auto cmd = ToCharPointers(command_);
398
399 if (!options.Strace().empty()) {
400 auto strace_args = {
401 "/usr/bin/strace",
402 "--daemonize",
403 "--output-separately", // Add .pid suffix
404 "--follow-forks",
405 "-o", // Write to a separate file.
406 options.Strace().c_str(),
407 };
408 cmd.insert(cmd.begin(), strace_args);
409 }
410
411 if (!validate_redirects(redirects_, inherited_fds_)) {
412 return Subprocess(-1, {});
413 }
414
415 pid_t pid = fork();
416 if (!pid) {
417 #ifdef __linux__
418 if (options.ExitWithParent()) {
419 prctl(PR_SET_PDEATHSIG, SIGHUP); // Die when parent dies
420 }
421 #endif
422
423 do_redirects(redirects_);
424
425 for (auto& prerequisite : prerequisites_) {
426 auto prerequisiteResult = prerequisite();
427
428 if (!prerequisiteResult.ok()) {
429 LOG(ERROR) << "Failed to check prerequisites: "
430 << prerequisiteResult.error().FormatForEnv();
431 }
432 }
433
434 if (options.InGroup()) {
435 // This call should never fail (see SETPGID(2))
436 if (setpgid(0, 0) != 0) {
437 auto error = errno;
438 LOG(ERROR) << "setpgid failed (" << strerror(error) << ")";
439 }
440 }
441 for (const auto& entry : inherited_fds_) {
442 if (fcntl(entry.second, F_SETFD, 0)) {
443 int error_num = errno;
444 LOG(ERROR) << "fcntl failed: " << strerror(error_num);
445 }
446 }
447 if (working_directory_->IsOpen()) {
448 if (SharedFD::Fchdir(working_directory_) != 0) {
449 LOG(ERROR) << "Fchdir failed: " << working_directory_->StrError();
450 }
451 }
452 int rval;
453 auto envp = ToCharPointers(env_);
454 const char* executable = executable_ ? executable_->c_str() : cmd[0];
455 #ifdef __linux__
456 rval = execvpe(executable, const_cast<char* const*>(cmd.data()),
457 const_cast<char* const*>(envp.data()));
458 #elif defined(__APPLE__)
459 rval = execve(executable, const_cast<char* const*>(cmd.data()),
460 const_cast<char* const*>(envp.data()));
461 #else
462 #error "Unsupported architecture"
463 #endif
464 // No need for an if: if exec worked it wouldn't have returned
465 LOG(ERROR) << "exec of " << cmd[0] << " with path \"" << executable
466 << "\" failed (" << strerror(errno) << ")";
467 exit(rval);
468 }
469 if (pid == -1) {
470 LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
471 }
472 if (options.Verbose()) { // "more verbose", and LOG(DEBUG) > LOG(VERBOSE)
473 LOG(DEBUG) << "Started (pid: " << pid << "): " << cmd[0];
474 for (int i = 1; cmd[i]; i++) {
475 LOG(DEBUG) << cmd[i];
476 }
477 } else {
478 LOG(VERBOSE) << "Started (pid: " << pid << "): " << cmd[0];
479 for (int i = 1; cmd[i]; i++) {
480 LOG(VERBOSE) << cmd[i];
481 }
482 }
483 return Subprocess(pid, subprocess_stopper_);
484 }
485
AsBashScript(const std::string & redirected_stdio_path) const486 std::string Command::AsBashScript(
487 const std::string& redirected_stdio_path) const {
488 CHECK(inherited_fds_.empty())
489 << "Bash wrapper will not have inheritied file descriptors.";
490 CHECK(redirects_.empty()) << "Bash wrapper will not have redirected stdio.";
491
492 std::string contents =
493 "#!/bin/bash\n\n" + android::base::Join(command_, " \\\n");
494 if (!redirected_stdio_path.empty()) {
495 contents += " &> " + AbsolutePath(redirected_stdio_path);
496 }
497 return contents;
498 }
499
500 // A class that waits for threads to exit in its destructor.
501 class ThreadJoiner {
502 std::vector<std::thread*> threads_;
503 public:
ThreadJoiner(const std::vector<std::thread * > threads)504 ThreadJoiner(const std::vector<std::thread*> threads) : threads_(threads) {}
~ThreadJoiner()505 ~ThreadJoiner() {
506 for (auto& thread : threads_) {
507 if (thread->joinable()) {
508 thread->join();
509 }
510 }
511 }
512 };
513
RunWithManagedStdio(Command && cmd_tmp,const std::string * stdin_str,std::string * stdout_str,std::string * stderr_str,SubprocessOptions options)514 int RunWithManagedStdio(Command&& cmd_tmp, const std::string* stdin_str,
515 std::string* stdout_str, std::string* stderr_str,
516 SubprocessOptions options) {
517 /*
518 * The order of these declarations is necessary for safety. If the function
519 * returns at any point, the Command will be destroyed first, closing all
520 * of its references to SharedFDs. This will cause the thread internals to fail
521 * their reads or writes. The ThreadJoiner then waits for the threads to
522 * complete, as running the destructor of an active std::thread crashes the
523 * program.
524 *
525 * C++ scoping rules dictate that objects are descoped in reverse order to
526 * construction, so this behavior is predictable.
527 */
528 std::thread stdin_thread, stdout_thread, stderr_thread;
529 ThreadJoiner thread_joiner({&stdin_thread, &stdout_thread, &stderr_thread});
530 Command cmd = std::move(cmd_tmp);
531 bool io_error = false;
532 if (stdin_str != nullptr) {
533 SharedFD pipe_read, pipe_write;
534 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
535 LOG(ERROR) << "Could not create a pipe to write the stdin of \""
536 << cmd.GetShortName() << "\"";
537 return -1;
538 }
539 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read);
540 stdin_thread = std::thread([pipe_write, stdin_str, &io_error]() {
541 int written = WriteAll(pipe_write, *stdin_str);
542 if (written < 0) {
543 io_error = true;
544 LOG(ERROR) << "Error in writing stdin to process";
545 }
546 });
547 }
548 if (stdout_str != nullptr) {
549 SharedFD pipe_read, pipe_write;
550 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
551 LOG(ERROR) << "Could not create a pipe to read the stdout of \""
552 << cmd.GetShortName() << "\"";
553 return -1;
554 }
555 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write);
556 stdout_thread = std::thread([pipe_read, stdout_str, &io_error]() {
557 int read = ReadAll(pipe_read, stdout_str);
558 if (read < 0) {
559 io_error = true;
560 LOG(ERROR) << "Error in reading stdout from process";
561 }
562 });
563 }
564 if (stderr_str != nullptr) {
565 SharedFD pipe_read, pipe_write;
566 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
567 LOG(ERROR) << "Could not create a pipe to read the stderr of \""
568 << cmd.GetShortName() << "\"";
569 return -1;
570 }
571 cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write);
572 stderr_thread = std::thread([pipe_read, stderr_str, &io_error]() {
573 int read = ReadAll(pipe_read, stderr_str);
574 if (read < 0) {
575 io_error = true;
576 LOG(ERROR) << "Error in reading stderr from process";
577 }
578 });
579 }
580
581 auto subprocess = cmd.Start(std::move(options));
582 if (!subprocess.Started()) {
583 return -1;
584 }
585 auto cmd_short_name = cmd.GetShortName();
586 {
587 // Force the destructor to run by moving it into a smaller scope.
588 // This is necessary to close the write end of the pipe.
589 Command forceDelete = std::move(cmd);
590 }
591
592 int code = subprocess.Wait();
593 {
594 auto join_threads = std::move(thread_joiner);
595 }
596 if (io_error) {
597 LOG(ERROR) << "IO error communicating with " << cmd_short_name;
598 return -1;
599 }
600 return code;
601 }
602
603 namespace {
604
605 struct ExtraParam {
606 // option for Subprocess::Start()
607 SubprocessOptions subprocess_options;
608 // options for Subprocess::Wait(...)
609 int wait_options;
610 siginfo_t* infop;
611 };
ExecuteImpl(const std::vector<std::string> & command,const std::optional<std::vector<std::string>> & envs,std::optional<ExtraParam> extra_param)612 Result<int> ExecuteImpl(const std::vector<std::string>& command,
613 const std::optional<std::vector<std::string>>& envs,
614 std::optional<ExtraParam> extra_param) {
615 Command cmd(command[0]);
616 for (size_t i = 1; i < command.size(); ++i) {
617 cmd.AddParameter(command[i]);
618 }
619 if (envs) {
620 cmd.SetEnvironment(*envs);
621 }
622 auto subprocess =
623 (!extra_param ? cmd.Start()
624 : cmd.Start(std::move(extra_param->subprocess_options)));
625 CF_EXPECT(subprocess.Started(), "Subprocess failed to start.");
626
627 if (extra_param) {
628 CF_EXPECT(extra_param->infop != nullptr,
629 "When ExtraParam is given, the infop buffer address "
630 << "must not be nullptr.");
631 return subprocess.Wait(extra_param->infop, extra_param->wait_options);
632 } else {
633 return subprocess.Wait();
634 }
635 }
636
637 } // namespace
638
Execute(const std::vector<std::string> & commands,const std::vector<std::string> & envs)639 int Execute(const std::vector<std::string>& commands,
640 const std::vector<std::string>& envs) {
641 auto result = ExecuteImpl(commands, envs, /* extra_param */ std::nullopt);
642 return (!result.ok() ? -1 : *result);
643 }
644
Execute(const std::vector<std::string> & commands)645 int Execute(const std::vector<std::string>& commands) {
646 std::vector<std::string> envs;
647 auto result = ExecuteImpl(commands, /* envs */ std::nullopt,
648 /* extra_param */ std::nullopt);
649 return (!result.ok() ? -1 : *result);
650 }
651
Execute(const std::vector<std::string> & commands,SubprocessOptions subprocess_options,int wait_options)652 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
653 SubprocessOptions subprocess_options,
654 int wait_options) {
655 siginfo_t info;
656 auto ret_code = CF_EXPECT(ExecuteImpl(
657 commands, /* envs */ std::nullopt,
658 ExtraParam{.subprocess_options = std::move(subprocess_options),
659 .wait_options = wait_options,
660 .infop = &info}));
661 CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
662 return info;
663 }
664
Execute(const std::vector<std::string> & commands,const std::vector<std::string> & envs,SubprocessOptions subprocess_options,int wait_options)665 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
666 const std::vector<std::string>& envs,
667 SubprocessOptions subprocess_options,
668 int wait_options) {
669 siginfo_t info;
670 auto ret_code = CF_EXPECT(ExecuteImpl(
671 commands, envs,
672 ExtraParam{.subprocess_options = std::move(subprocess_options),
673 .wait_options = wait_options,
674 .infop = &info}));
675 CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
676 return info;
677 }
678
679 } // namespace cuttlefish
680