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 #pragma once
17 
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 
21 #include <atomic>
22 #include <cstdio>
23 #include <cstring>
24 #include <functional>
25 #include <map>
26 #include <optional>
27 #include <ostream>
28 #include <sstream>
29 #include <string>
30 #include <type_traits>
31 #include <unordered_map>
32 #include <utility>
33 #include <vector>
34 
35 #include <android-base/logging.h>
36 #include <android-base/strings.h>
37 
38 #include "common/libs/fs/shared_fd.h"
39 
40 namespace cuttlefish {
41 
42 /*
43  * Does what ArgsToVec(int argc, char**) from flag_parser.h does
44  * without argc.
45  */
46 std::vector<std::string> ArgsToVec(char** argv);
47 std::unordered_map<std::string, std::string> EnvpToMap(char** envp);
48 
49 enum class StopperResult {
50   kStopFailure, /* Failed to stop the subprocess. */
51   kStopCrash,   /* Attempted to stop the subprocess cleanly, but that failed. */
52   kStopSuccess, /* The subprocess exited in the expected way. */
53 };
54 
55 class Subprocess;
56 using SubprocessStopper = std::function<StopperResult(Subprocess*)>;
57 // Kills a process by sending it the SIGKILL signal.
58 StopperResult KillSubprocess(Subprocess* subprocess);
59 /* Creates a `SubprocessStopper` that first tries `nice_stopper` then falls back
60  * to `KillSubprocess` if that fails. */
61 SubprocessStopper KillSubprocessFallback(std::function<StopperResult()>);
62 SubprocessStopper KillSubprocessFallback(SubprocessStopper nice_stopper);
63 
64 // Keeps track of a running (sub)process. Allows to wait for its completion.
65 // It's an error to wait twice for the same subprocess.
66 class Subprocess {
67  public:
68   enum class StdIOChannel {
69     kStdIn = 0,
70     kStdOut = 1,
71     kStdErr = 2,
72   };
73 
74   Subprocess(pid_t pid, SubprocessStopper stopper = KillSubprocess)
pid_(pid)75       : pid_(pid), started_(pid > 0), stopper_(stopper) {}
76   // The default implementation won't do because we need to reset the pid of the
77   // moved object.
78   Subprocess(Subprocess&&);
79   ~Subprocess() = default;
80   Subprocess& operator=(Subprocess&&);
81   // Waits for the subprocess to complete. Returns zero if completed
82   // successfully, non-zero otherwise.
83   int Wait();
84   // Same as waitid(2)
85   int Wait(siginfo_t* infop, int options);
86   // Whether the command started successfully. It only says whether the call to
87   // fork() succeeded or not, it says nothing about exec or successful
88   // completion of the command, that's what Wait is for.
Started()89   bool Started() const { return started_; }
pid()90   pid_t pid() const { return pid_; }
Stop()91   StopperResult Stop() { return stopper_(this); }
92 
93   Result<void> SendSignal(const int signal);
94   Result<void> SendSignalToGroup(const int signal);
95 
96  private:
97   // Copy is disabled to avoid waiting twice for the same pid (the first wait
98   // frees the pid, which allows the kernel to reuse it so we may end up waiting
99   // for the wrong process)
100   Subprocess(const Subprocess&) = delete;
101   Subprocess& operator=(const Subprocess&) = delete;
102   std::atomic<pid_t> pid_ = -1;
103   bool started_ = false;
104   SubprocessStopper stopper_;
105 };
106 
107 class SubprocessOptions {
108  public:
SubprocessOptions()109   SubprocessOptions()
110       : verbose_(true), exit_with_parent_(true), in_group_(false) {}
111   SubprocessOptions& Verbose(bool verbose) &;
112   SubprocessOptions Verbose(bool verbose) &&;
113   SubprocessOptions& ExitWithParent(bool exit_with_parent) &;
114   SubprocessOptions ExitWithParent(bool exit_with_parent) &&;
115   SubprocessOptions& SandboxArguments(std::vector<std::string>) &;
116   SubprocessOptions SandboxArguments(std::vector<std::string>) &&;
117   // The subprocess runs as head of its own process group.
118   SubprocessOptions& InGroup(bool in_group) &;
119   SubprocessOptions InGroup(bool in_group) &&;
120 
121   SubprocessOptions& Strace(std::string strace_output_path) &;
122   SubprocessOptions Strace(std::string strace_output_path) &&;
123 
Verbose()124   bool Verbose() const { return verbose_; }
ExitWithParent()125   bool ExitWithParent() const { return exit_with_parent_; }
SandboxArguments()126   const std::vector<std::string>& SandboxArguments() const {
127     return sandbox_arguments_;
128   }
InGroup()129   bool InGroup() const { return in_group_; }
Strace()130   const std::string& Strace() const { return strace_; }
131 
132  private:
133   bool verbose_;
134   bool exit_with_parent_;
135   std::vector<std::string> sandbox_arguments_;
136   bool in_group_;
137   std::string strace_;
138 };
139 
140 // An executable command. Multiple subprocesses can be started from the same
141 // command object. This class owns any file descriptors that the subprocess
142 // should inherit.
143 class Command {
144  private:
145   template <typename T>
146   // For every type other than SharedFD (for which there is a specialisation)
BuildParameter(std::stringstream * stream,T t)147   void BuildParameter(std::stringstream* stream, T t) {
148     *stream << t;
149   }
150   // Special treatment for SharedFD
151   void BuildParameter(std::stringstream* stream, SharedFD shared_fd);
152   template <typename T, typename... Args>
BuildParameter(std::stringstream * stream,T t,Args...args)153   void BuildParameter(std::stringstream* stream, T t, Args... args) {
154     BuildParameter(stream, t);
155     BuildParameter(stream, args...);
156   }
157 
158  public:
159   // Constructs a command object from the path to an executable binary and an
160   // optional subprocess stopper. When not provided, stopper defaults to sending
161   // SIGKILL to the subprocess.
162   Command(std::string executable, SubprocessStopper stopper = KillSubprocess);
163   Command(Command&&) = default;
164   // The default copy constructor is unsafe because it would mean multiple
165   // closing of the inherited file descriptors. If needed it can be implemented
166   // using dup(2)
167   Command(const Command&) = delete;
168   Command& operator=(const Command&) = delete;
169   ~Command();
170 
Executable()171   const std::string& Executable() const {
172     return executable_ ? *executable_ : command_[0];
173   }
174 
SetExecutable(std::string executable)175   Command& SetExecutable(std::string executable) & {
176     executable_ = std::move(executable);
177     return *this;
178   }
SetExecutable(std::string executable)179   Command SetExecutable(std::string executable) && {
180     return std::move(SetExecutable(executable));
181   }
182 
SetName(std::string name)183   Command& SetName(std::string name) & {
184     command_[0] = std::move(name);
185     return *this;
186   }
SetName(std::string name)187   Command SetName(std::string name) && {
188     return std::move(SetName(std::move(name)));
189   }
190 
SetExecutableAndName(std::string name)191   Command& SetExecutableAndName(std::string name) & {
192     return SetExecutable(name).SetName(std::move(name));
193   }
194 
SetExecutableAndName(std::string name)195   Command SetExecutableAndName(std::string name) && {
196     return std::move(SetExecutableAndName(std::move(name)));
197   }
198 
SetStopper(SubprocessStopper stopper)199   Command& SetStopper(SubprocessStopper stopper) & {
200     subprocess_stopper_ = std::move(stopper);
201     return *this;
202   }
SetStopper(SubprocessStopper stopper)203   Command SetStopper(SubprocessStopper stopper) && {
204     return std::move(SetStopper(std::move(stopper)));
205   }
206 
207   // Specify the environment for the subprocesses to be started. By default
208   // subprocesses inherit the parent's environment.
SetEnvironment(std::vector<std::string> env)209   Command& SetEnvironment(std::vector<std::string> env) & {
210     env_ = std::move(env);
211     return *this;
212   }
SetEnvironment(std::vector<std::string> env)213   Command SetEnvironment(std::vector<std::string> env) && {
214     return std::move(SetEnvironment(std::move(env)));
215   }
216 
AddEnvironmentVariable(const std::string & env_var,const std::string & value)217   Command& AddEnvironmentVariable(const std::string& env_var,
218                                   const std::string& value) & {
219     return AddEnvironmentVariable(env_var + "=" + value);
220   }
AddEnvironmentVariable(const std::string & env_var,const std::string & value)221   Command AddEnvironmentVariable(const std::string& env_var,
222                                  const std::string& value) && {
223     AddEnvironmentVariable(env_var, value);
224     return std::move(*this);
225   }
226 
AddEnvironmentVariable(std::string env_var)227   Command& AddEnvironmentVariable(std::string env_var) & {
228     env_.emplace_back(std::move(env_var));
229     return *this;
230   }
AddEnvironmentVariable(std::string env_var)231   Command AddEnvironmentVariable(std::string env_var) && {
232     return std::move(AddEnvironmentVariable(std::move(env_var)));
233   }
234 
235   // Specify an environment variable to be unset from the parent's
236   // environment for the subprocesses to be started.
UnsetFromEnvironment(const std::string & env_var)237   Command& UnsetFromEnvironment(const std::string& env_var) & {
238     auto it = env_.begin();
239     while (it != env_.end()) {
240       if (android::base::StartsWith(*it, env_var + "=")) {
241         it = env_.erase(it);
242       } else {
243         ++it;
244       }
245     }
246     return *this;
247   }
UnsetFromEnvironment(const std::string & env_var)248   Command UnsetFromEnvironment(const std::string& env_var) && {
249     return std::move(UnsetFromEnvironment(env_var));
250   }
251 
252   // Adds a single parameter to the command. All arguments are concatenated into
253   // a single string to form a parameter. If one of those arguments is a
254   // SharedFD a duplicate of it will be used and won't be closed until the
255   // object is destroyed. To add multiple parameters to the command the function
256   // must be called multiple times, one per parameter.
257   template <typename... Args>
AddParameter(Args...args)258   Command& AddParameter(Args... args) & {
259     std::stringstream ss;
260     BuildParameter(&ss, args...);
261     command_.push_back(ss.str());
262     return *this;
263   }
264   template <typename... Args>
AddParameter(Args...args)265   Command AddParameter(Args... args) && {
266     return std::move(AddParameter(std::forward<Args>(args)...));
267   }
268   // Similar to AddParameter, except the args are appended to the last (most
269   // recently-added) parameter in the command.
270   template <typename... Args>
AppendToLastParameter(Args...args)271   Command& AppendToLastParameter(Args... args) & {
272     CHECK(!command_.empty()) << "There is no parameter to append to.";
273     std::stringstream ss;
274     BuildParameter(&ss, args...);
275     command_[command_.size() - 1] += ss.str();
276     return *this;
277   }
278   template <typename... Args>
AppendToLastParameter(Args...args)279   Command AppendToLastParameter(Args... args) && {
280     return std::move(AppendToLastParameter(std::forward<Args>(args)...));
281   }
282 
283   // Redirects the standard IO of the command.
284   Command& RedirectStdIO(Subprocess::StdIOChannel channel,
285                          SharedFD shared_fd) &;
286   Command RedirectStdIO(Subprocess::StdIOChannel channel,
287                         SharedFD shared_fd) &&;
288   Command& RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
289                          Subprocess::StdIOChannel parent_channel) &;
290   Command RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
291                         Subprocess::StdIOChannel parent_channel) &&;
292 
293   Command& SetWorkingDirectory(const std::string& path) &;
294   Command SetWorkingDirectory(const std::string& path) &&;
295   Command& SetWorkingDirectory(SharedFD dirfd) &;
296   Command SetWorkingDirectory(SharedFD dirfd) &&;
297 
298   Command& AddPrerequisite(const std::function<Result<void>()>& prerequisite) &;
299   Command AddPrerequisite(const std::function<Result<void>()>& prerequisite) &&;
300 
301   // Starts execution of the command. This method can be called multiple times,
302   // effectively staring multiple (possibly concurrent) instances.
303   Subprocess Start(SubprocessOptions options = SubprocessOptions()) const;
304 
GetShortName()305   std::string GetShortName() const {
306     // This is safe because the constructor guarantees the name of the binary to
307     // be at index 0 on the vector
308     return command_[0];
309   }
310 
311   // Generates the contents for a bash script that can be used to run this
312   // command. Note that this command must not require any file descriptors
313   // or stdio redirects as those would not be available when the bash script
314   // is run.
315   std::string AsBashScript(const std::string& redirected_stdio_path = "") const;
316 
317  private:
318   std::optional<std::string> executable_;  // When unset, use command_[0]
319   std::vector<std::string> command_;
320   std::vector<std::function<Result<void>()>> prerequisites_;
321   std::map<SharedFD, int> inherited_fds_{};
322   std::map<Subprocess::StdIOChannel, int> redirects_{};
323   std::vector<std::string> env_{};
324   SubprocessStopper subprocess_stopper_;
325   SharedFD working_directory_;
326 };
327 
328 /*
329  * Consumes a Command and runs it, optionally managing the stdio channels.
330  *
331  * If `stdin` is set, the subprocess stdin will be pipe providing its contents.
332  * If `stdout` is set, the subprocess stdout will be captured and saved to it.
333  * If `stderr` is set, the subprocess stderr will be captured and saved to it.
334  *
335  * If `command` exits normally, the lower 8 bits of the return code will be
336  * returned in a value between 0 and 255.
337  * If some setup fails, `command` fails to start, or `command` exits due to a
338  * signal, the return value will be negative.
339  */
340 int RunWithManagedStdio(Command&& command, const std::string* stdin,
341                         std::string* stdout, std::string* stderr,
342                         SubprocessOptions options = SubprocessOptions());
343 
344 /**
345  * Returns the exit status on success, negative values on error
346  *
347  * If failed in fork() or exec(), returns -1.
348  * If the child exited from an unhandled signal, returns -1.
349  * Otherwise, returns the exit status.
350  *
351  * TODO: Changes return type to Result<int>
352  *
353  *   For now, too many callsites expects int, and needs quite a lot of changes
354  *   if we change the return type.
355  */
356 int Execute(const std::vector<std::string>& commands);
357 int Execute(const std::vector<std::string>& commands,
358             const std::vector<std::string>& envs);
359 
360 /**
361  * Similar as the two above but returns CF_ERR instead of -1, and siginfo_t
362  * instead of the exit status.
363  */
364 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
365                           SubprocessOptions subprocess_options,
366                           int wait_options);
367 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
368                           const std::vector<std::string>& envs,
369                           SubprocessOptions subprocess_options,
370                           int wait_options);
371 
372 }  // namespace cuttlefish
373