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_; }
InGroup()126   bool InGroup() const { return in_group_; }
Strace()127   const std::string& Strace() const { return strace_; }
128 
129  private:
130   bool verbose_;
131   bool exit_with_parent_;
132   bool in_group_;
133   std::string strace_;
134 };
135 
136 // An executable command. Multiple subprocesses can be started from the same
137 // command object. This class owns any file descriptors that the subprocess
138 // should inherit.
139 class Command {
140  private:
141   template <typename T>
142   // For every type other than SharedFD (for which there is a specialisation)
BuildParameter(std::stringstream * stream,T t)143   void BuildParameter(std::stringstream* stream, T t) {
144     *stream << t;
145   }
146   // Special treatment for SharedFD
147   void BuildParameter(std::stringstream* stream, SharedFD shared_fd);
148   template <typename T, typename... Args>
BuildParameter(std::stringstream * stream,T t,Args...args)149   void BuildParameter(std::stringstream* stream, T t, Args... args) {
150     BuildParameter(stream, t);
151     BuildParameter(stream, args...);
152   }
153 
154  public:
155   // Constructs a command object from the path to an executable binary and an
156   // optional subprocess stopper. When not provided, stopper defaults to sending
157   // SIGKILL to the subprocess.
158   Command(std::string executable, SubprocessStopper stopper = KillSubprocess);
159   Command(Command&&) = default;
160   // The default copy constructor is unsafe because it would mean multiple
161   // closing of the inherited file descriptors. If needed it can be implemented
162   // using dup(2)
163   Command(const Command&) = delete;
164   Command& operator=(const Command&) = delete;
165   ~Command();
166 
Executable()167   const std::string& Executable() const {
168     return executable_ ? *executable_ : command_[0];
169   }
170 
SetExecutable(std::string executable)171   Command& SetExecutable(std::string executable) & {
172     executable_ = std::move(executable);
173     return *this;
174   }
SetExecutable(std::string executable)175   Command SetExecutable(std::string executable) && {
176     return std::move(SetExecutable(executable));
177   }
178 
SetName(std::string name)179   Command& SetName(std::string name) & {
180     command_[0] = std::move(name);
181     return *this;
182   }
SetName(std::string name)183   Command SetName(std::string name) && {
184     return std::move(SetName(std::move(name)));
185   }
186 
SetExecutableAndName(std::string name)187   Command& SetExecutableAndName(std::string name) & {
188     return SetExecutable(name).SetName(std::move(name));
189   }
190 
SetExecutableAndName(std::string name)191   Command SetExecutableAndName(std::string name) && {
192     return std::move(SetExecutableAndName(std::move(name)));
193   }
194 
SetStopper(SubprocessStopper stopper)195   Command& SetStopper(SubprocessStopper stopper) & {
196     subprocess_stopper_ = std::move(stopper);
197     return *this;
198   }
SetStopper(SubprocessStopper stopper)199   Command SetStopper(SubprocessStopper stopper) && {
200     return std::move(SetStopper(std::move(stopper)));
201   }
202 
203   // Specify the environment for the subprocesses to be started. By default
204   // subprocesses inherit the parent's environment.
SetEnvironment(std::vector<std::string> env)205   Command& SetEnvironment(std::vector<std::string> env) & {
206     env_ = std::move(env);
207     return *this;
208   }
SetEnvironment(std::vector<std::string> env)209   Command SetEnvironment(std::vector<std::string> env) && {
210     return std::move(SetEnvironment(std::move(env)));
211   }
212 
AddEnvironmentVariable(const std::string & env_var,const std::string & value)213   Command& AddEnvironmentVariable(const std::string& env_var,
214                                   const std::string& value) & {
215     return AddEnvironmentVariable(env_var + "=" + value);
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     AddEnvironmentVariable(env_var, value);
220     return std::move(*this);
221   }
222 
AddEnvironmentVariable(std::string env_var)223   Command& AddEnvironmentVariable(std::string env_var) & {
224     env_.emplace_back(std::move(env_var));
225     return *this;
226   }
AddEnvironmentVariable(std::string env_var)227   Command AddEnvironmentVariable(std::string env_var) && {
228     return std::move(AddEnvironmentVariable(std::move(env_var)));
229   }
230 
231   // Specify an environment variable to be unset from the parent's
232   // environment for the subprocesses to be started.
UnsetFromEnvironment(const std::string & env_var)233   Command& UnsetFromEnvironment(const std::string& env_var) & {
234     auto it = env_.begin();
235     while (it != env_.end()) {
236       if (android::base::StartsWith(*it, env_var + "=")) {
237         it = env_.erase(it);
238       } else {
239         ++it;
240       }
241     }
242     return *this;
243   }
UnsetFromEnvironment(const std::string & env_var)244   Command UnsetFromEnvironment(const std::string& env_var) && {
245     return std::move(UnsetFromEnvironment(env_var));
246   }
247 
248   // Adds a single parameter to the command. All arguments are concatenated into
249   // a single string to form a parameter. If one of those arguments is a
250   // SharedFD a duplicate of it will be used and won't be closed until the
251   // object is destroyed. To add multiple parameters to the command the function
252   // must be called multiple times, one per parameter.
253   template <typename... Args>
AddParameter(Args...args)254   Command& AddParameter(Args... args) & {
255     std::stringstream ss;
256     BuildParameter(&ss, args...);
257     command_.push_back(ss.str());
258     return *this;
259   }
260   template <typename... Args>
AddParameter(Args...args)261   Command AddParameter(Args... args) && {
262     return std::move(AddParameter(std::forward<Args>(args)...));
263   }
264   // Similar to AddParameter, except the args are appended to the last (most
265   // recently-added) parameter in the command.
266   template <typename... Args>
AppendToLastParameter(Args...args)267   Command& AppendToLastParameter(Args... args) & {
268     CHECK(!command_.empty()) << "There is no parameter to append to.";
269     std::stringstream ss;
270     BuildParameter(&ss, args...);
271     command_[command_.size() - 1] += ss.str();
272     return *this;
273   }
274   template <typename... Args>
AppendToLastParameter(Args...args)275   Command AppendToLastParameter(Args... args) && {
276     return std::move(AppendToLastParameter(std::forward<Args>(args)...));
277   }
278 
279   // Redirects the standard IO of the command.
280   Command& RedirectStdIO(Subprocess::StdIOChannel channel,
281                          SharedFD shared_fd) &;
282   Command RedirectStdIO(Subprocess::StdIOChannel channel,
283                         SharedFD shared_fd) &&;
284   Command& RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
285                          Subprocess::StdIOChannel parent_channel) &;
286   Command RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
287                         Subprocess::StdIOChannel parent_channel) &&;
288 
289   Command& SetWorkingDirectory(const std::string& path) &;
290   Command SetWorkingDirectory(const std::string& path) &&;
291   Command& SetWorkingDirectory(SharedFD dirfd) &;
292   Command SetWorkingDirectory(SharedFD dirfd) &&;
293 
294   Command& AddPrerequisite(const std::function<Result<void>()>& prerequisite) &;
295   Command AddPrerequisite(const std::function<Result<void>()>& prerequisite) &&;
296 
297   // Starts execution of the command. This method can be called multiple times,
298   // effectively staring multiple (possibly concurrent) instances.
299   Subprocess Start(SubprocessOptions options = SubprocessOptions()) const;
300 
GetShortName()301   std::string GetShortName() const {
302     // This is safe because the constructor guarantees the name of the binary to
303     // be at index 0 on the vector
304     return command_[0];
305   }
306 
307   // Generates the contents for a bash script that can be used to run this
308   // command. Note that this command must not require any file descriptors
309   // or stdio redirects as those would not be available when the bash script
310   // is run.
311   std::string AsBashScript(const std::string& redirected_stdio_path = "") const;
312 
313  private:
314   std::optional<std::string> executable_;  // When unset, use command_[0]
315   std::vector<std::string> command_;
316   std::vector<std::function<Result<void>()>> prerequisites_;
317   std::map<SharedFD, int> inherited_fds_{};
318   std::map<Subprocess::StdIOChannel, int> redirects_{};
319   std::vector<std::string> env_{};
320   SubprocessStopper subprocess_stopper_;
321   SharedFD working_directory_;
322 };
323 
324 /*
325  * Consumes a Command and runs it, optionally managing the stdio channels.
326  *
327  * If `stdin` is set, the subprocess stdin will be pipe providing its contents.
328  * If `stdout` is set, the subprocess stdout will be captured and saved to it.
329  * If `stderr` is set, the subprocess stderr will be captured and saved to it.
330  *
331  * If `command` exits normally, the lower 8 bits of the return code will be
332  * returned in a value between 0 and 255.
333  * If some setup fails, `command` fails to start, or `command` exits due to a
334  * signal, the return value will be negative.
335  */
336 int RunWithManagedStdio(Command&& command, const std::string* stdin,
337                         std::string* stdout, std::string* stderr,
338                         SubprocessOptions options = SubprocessOptions());
339 
340 /**
341  * Returns the exit status on success, negative values on error
342  *
343  * If failed in fork() or exec(), returns -1.
344  * If the child exited from an unhandled signal, returns -1.
345  * Otherwise, returns the exit status.
346  *
347  * TODO: Changes return type to Result<int>
348  *
349  *   For now, too many callsites expects int, and needs quite a lot of changes
350  *   if we change the return type.
351  */
352 int Execute(const std::vector<std::string>& commands);
353 int Execute(const std::vector<std::string>& commands,
354             const std::vector<std::string>& envs);
355 
356 /**
357  * Similar as the two above but returns CF_ERR instead of -1, and siginfo_t
358  * instead of the exit status.
359  */
360 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
361                           SubprocessOptions subprocess_options,
362                           int wait_options);
363 Result<siginfo_t> Execute(const std::vector<std::string>& commands,
364                           const std::vector<std::string>& envs,
365                           SubprocessOptions subprocess_options,
366                           int wait_options);
367 
368 }  // namespace cuttlefish
369