• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   for (auto& prerequisite : prerequisites_) {
416     auto prerequisiteResult = prerequisite();
417 
418     if (!prerequisiteResult.ok()) {
419       LOG(ERROR) << "Failed to check prerequisites: "
420                  << prerequisiteResult.error().FormatForEnv();
421     }
422   }
423 
424   // ToCharPointers allocates memory so it can't be called in the child process.
425   auto envp = ToCharPointers(env_);
426   pid_t pid = fork();
427   if (!pid) {
428     // LOG(...) can't be used in the child process because it may block waiting
429     // for other threads which don't exist in the child process.
430 #ifdef __linux__
431     if (options.ExitWithParent()) {
432       prctl(PR_SET_PDEATHSIG, SIGHUP); // Die when parent dies
433     }
434 #endif
435 
436     do_redirects(redirects_);
437 
438     if (options.InGroup()) {
439       // This call should never fail (see SETPGID(2))
440       if (setpgid(0, 0) != 0) {
441         exit(-errno);
442       }
443     }
444     for (const auto& entry : inherited_fds_) {
445       if (fcntl(entry.second, F_SETFD, 0)) {
446         exit(-errno);
447       }
448     }
449     if (working_directory_->IsOpen()) {
450       if (SharedFD::Fchdir(working_directory_) != 0) {
451         exit(-errno);
452       }
453     }
454     int rval;
455     const char* executable = executable_ ? executable_->c_str() : cmd[0];
456 #ifdef __linux__
457     rval = execvpe(executable, const_cast<char* const*>(cmd.data()),
458                    const_cast<char* const*>(envp.data()));
459 #elif defined(__APPLE__)
460     rval = execve(executable, const_cast<char* const*>(cmd.data()),
461                   const_cast<char* const*>(envp.data()));
462 #else
463 #error "Unsupported architecture"
464 #endif
465     // No need to check for error, execvpe/execve don't return on success.
466     exit(rval);
467   }
468   if (pid == -1) {
469     LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
470   }
471   if (options.Verbose()) { // "more verbose", and LOG(DEBUG) > LOG(VERBOSE)
472     LOG(DEBUG) << "Started (pid: " << pid << "): " << cmd[0];
473     for (int i = 1; cmd[i]; i++) {
474       LOG(DEBUG) << cmd[i];
475     }
476   } else {
477     LOG(VERBOSE) << "Started (pid: " << pid << "): " << cmd[0];
478     for (int i = 1; cmd[i]; i++) {
479       LOG(VERBOSE) << cmd[i];
480     }
481   }
482   return Subprocess(pid, subprocess_stopper_);
483 }
484 
ToString() const485 std::string Command::ToString() const {
486   std::stringstream ss;
487   if (!env_.empty()) {
488     ss << android::base::Join(env_, " ");
489     ss << " ";
490   }
491   ss << android::base::Join(command_, " ");
492   return ss.str();
493 }
494 
AsBashScript(const std::string & redirected_stdio_path) const495 std::string Command::AsBashScript(
496     const std::string& redirected_stdio_path) const {
497   CHECK(inherited_fds_.empty())
498       << "Bash wrapper will not have inheritied file descriptors.";
499   CHECK(redirects_.empty()) << "Bash wrapper will not have redirected stdio.";
500 
501   std::string contents =
502       "#!/bin/bash\n\n" + android::base::Join(command_, " \\\n");
503   if (!redirected_stdio_path.empty()) {
504     contents += " &> " + AbsolutePath(redirected_stdio_path);
505   }
506   return contents;
507 }
508 
509 // A class that waits for threads to exit in its destructor.
510 class ThreadJoiner {
511 std::vector<std::thread*> threads_;
512 public:
ThreadJoiner(const std::vector<std::thread * > threads)513   ThreadJoiner(const std::vector<std::thread*> threads) : threads_(threads) {}
~ThreadJoiner()514   ~ThreadJoiner() {
515     for (auto& thread : threads_) {
516       if (thread->joinable()) {
517         thread->join();
518       }
519     }
520   }
521 };
522 
RunWithManagedStdio(Command && cmd_tmp,const std::string * stdin_str,std::string * stdout_str,std::string * stderr_str,SubprocessOptions options)523 int RunWithManagedStdio(Command&& cmd_tmp, const std::string* stdin_str,
524                         std::string* stdout_str, std::string* stderr_str,
525                         SubprocessOptions options) {
526   /*
527    * The order of these declarations is necessary for safety. If the function
528    * returns at any point, the Command will be destroyed first, closing all
529    * of its references to SharedFDs. This will cause the thread internals to fail
530    * their reads or writes. The ThreadJoiner then waits for the threads to
531    * complete, as running the destructor of an active std::thread crashes the
532    * program.
533    *
534    * C++ scoping rules dictate that objects are descoped in reverse order to
535    * construction, so this behavior is predictable.
536    */
537   std::thread stdin_thread, stdout_thread, stderr_thread;
538   ThreadJoiner thread_joiner({&stdin_thread, &stdout_thread, &stderr_thread});
539   Command cmd = std::move(cmd_tmp);
540   bool io_error = false;
541   if (stdin_str != nullptr) {
542     SharedFD pipe_read, pipe_write;
543     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
544       LOG(ERROR) << "Could not create a pipe to write the stdin of \""
545                 << cmd.GetShortName() << "\"";
546       return -1;
547     }
548     cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read);
549     stdin_thread = std::thread([pipe_write, stdin_str, &io_error]() {
550       int written = WriteAll(pipe_write, *stdin_str);
551       if (written < 0) {
552         io_error = true;
553         LOG(ERROR) << "Error in writing stdin to process";
554       }
555     });
556   }
557   if (stdout_str != nullptr) {
558     SharedFD pipe_read, pipe_write;
559     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
560       LOG(ERROR) << "Could not create a pipe to read the stdout of \""
561                 << cmd.GetShortName() << "\"";
562       return -1;
563     }
564     cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write);
565     stdout_thread = std::thread([pipe_read, stdout_str, &io_error]() {
566       int read = ReadAll(pipe_read, stdout_str);
567       if (read < 0) {
568         io_error = true;
569         LOG(ERROR) << "Error in reading stdout from process";
570       }
571     });
572   }
573   if (stderr_str != nullptr) {
574     SharedFD pipe_read, pipe_write;
575     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
576       LOG(ERROR) << "Could not create a pipe to read the stderr of \""
577                 << cmd.GetShortName() << "\"";
578       return -1;
579     }
580     cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write);
581     stderr_thread = std::thread([pipe_read, stderr_str, &io_error]() {
582       int read = ReadAll(pipe_read, stderr_str);
583       if (read < 0) {
584         io_error = true;
585         LOG(ERROR) << "Error in reading stderr from process";
586       }
587     });
588   }
589 
590   auto subprocess = cmd.Start(std::move(options));
591   if (!subprocess.Started()) {
592     return -1;
593   }
594   auto cmd_short_name = cmd.GetShortName();
595   {
596     // Force the destructor to run by moving it into a smaller scope.
597     // This is necessary to close the write end of the pipe.
598     Command forceDelete = std::move(cmd);
599   }
600 
601   int code = subprocess.Wait();
602   {
603     auto join_threads = std::move(thread_joiner);
604   }
605   if (io_error) {
606     LOG(ERROR) << "IO error communicating with " << cmd_short_name;
607     return -1;
608   }
609   return code;
610 }
611 
612 namespace {
613 
614 struct ExtraParam {
615   // option for Subprocess::Start()
616   SubprocessOptions subprocess_options;
617   // options for Subprocess::Wait(...)
618   int wait_options;
619   siginfo_t* infop;
620 };
ExecuteImpl(const std::vector<std::string> & command,const std::optional<std::vector<std::string>> & envs,std::optional<ExtraParam> extra_param)621 Result<int> ExecuteImpl(const std::vector<std::string>& command,
622                         const std::optional<std::vector<std::string>>& envs,
623                         std::optional<ExtraParam> extra_param) {
624   Command cmd(command[0]);
625   for (size_t i = 1; i < command.size(); ++i) {
626     cmd.AddParameter(command[i]);
627   }
628   if (envs) {
629     cmd.SetEnvironment(*envs);
630   }
631   auto subprocess =
632       (!extra_param ? cmd.Start()
633                     : cmd.Start(std::move(extra_param->subprocess_options)));
634   CF_EXPECT(subprocess.Started(), "Subprocess failed to start.");
635 
636   if (extra_param) {
637     CF_EXPECT(extra_param->infop != nullptr,
638               "When ExtraParam is given, the infop buffer address "
639                   << "must not be nullptr.");
640     return subprocess.Wait(extra_param->infop, extra_param->wait_options);
641   } else {
642     return subprocess.Wait();
643   }
644 }
645 
646 }  // namespace
647 
Execute(const std::vector<std::string> & commands,const std::vector<std::string> & envs)648 int Execute(const std::vector<std::string>& commands,
649             const std::vector<std::string>& envs) {
650   auto result = ExecuteImpl(commands, envs, /* extra_param */ std::nullopt);
651   return (!result.ok() ? -1 : *result);
652 }
653 
Execute(const std::vector<std::string> & commands)654 int Execute(const std::vector<std::string>& commands) {
655   std::vector<std::string> envs;
656   auto result = ExecuteImpl(commands, /* envs */ std::nullopt,
657                             /* extra_param */ std::nullopt);
658   return (!result.ok() ? -1 : *result);
659 }
660 
Execute(const std::vector<std::string> & commands,SubprocessOptions subprocess_options,int wait_options)661 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
662                           SubprocessOptions subprocess_options,
663                           int wait_options) {
664   siginfo_t info;
665   auto ret_code = CF_EXPECT(ExecuteImpl(
666       commands, /* envs */ std::nullopt,
667       ExtraParam{.subprocess_options = std::move(subprocess_options),
668                  .wait_options = wait_options,
669                  .infop = &info}));
670   CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
671   return info;
672 }
673 
Execute(const std::vector<std::string> & commands,const std::vector<std::string> & envs,SubprocessOptions subprocess_options,int wait_options)674 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
675                           const std::vector<std::string>& envs,
676                           SubprocessOptions subprocess_options,
677                           int wait_options) {
678   siginfo_t info;
679   auto ret_code = CF_EXPECT(ExecuteImpl(
680       commands, envs,
681       ExtraParam{.subprocess_options = std::move(subprocess_options),
682                  .wait_options = wait_options,
683                  .infop = &info}));
684   CF_EXPECT(ret_code == 0, "Subprocess::Wait() returned " << ret_code);
685   return info;
686 }
687 
688 }  // namespace cuttlefish
689