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