Android-cuttlefish cvd tool
subprocess.h
Go to the documentation of this file.
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
37
39
40namespace cuttlefish {
41
42/*
43 * Does what ArgsToVec(int argc, char**) from flag_parser.h does
44 * without argc.
45 */
46std::vector<std::string> ArgsToVec(char** argv);
47std::unordered_map<std::string, std::string> EnvpToMap(char** envp);
48
49enum 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
55class Subprocess;
57// Kills a process by sending it the SIGKILL signal.
59/* Creates a `SubprocessStopper` that first tries `nice_stopper` then falls back
60 * to `KillSubprocess` if that fails. */
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.
67 public:
68 enum class StdIOChannel {
69 kStdIn = 0,
70 kStdOut = 1,
71 kStdErr = 2,
72 };
73
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.
79 ~Subprocess() = default;
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.
89 bool Started() const { return started_; }
90 pid_t pid() const { return pid_; }
91 StopperResult Stop() { return stopper_(this); }
92
93 Result<void> SendSignal(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;
105};
106
108 public:
110 : verbose_(true), exit_with_parent_(true), in_group_(false) {}
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
124 bool Verbose() const { return verbose_; }
125 bool ExitWithParent() const { return exit_with_parent_; }
126 bool InGroup() const { return in_group_; }
127 const std::string& Strace() const { return strace_; }
128
129 private:
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.
139class Command {
140 private:
141 template <typename T>
142 // For every type other than SharedFD (for which there is a specialisation)
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>
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
167 const std::string& Executable() const {
168 return executable_ ? *executable_ : command_[0];
169 }
170
171 Command& SetExecutable(std::string executable) & {
172 executable_ = std::move(executable);
173 return *this;
174 }
175 Command SetExecutable(std::string executable) && {
176 return std::move(SetExecutable(executable));
177 }
178
179 Command& SetName(std::string name) & {
180 command_[0] = std::move(name);
181 return *this;
182 }
183 Command SetName(std::string name) && {
184 return std::move(SetName(std::move(name)));
185 }
186
187 Command& SetExecutableAndName(std::string name) & {
188 return SetExecutable(name).SetName(std::move(name));
189 }
190
191 Command SetExecutableAndName(std::string name) && {
192 return std::move(SetExecutableAndName(std::move(name)));
193 }
194
196 subprocess_stopper_ = std::move(stopper);
197 return *this;
198 }
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.
205 Command& SetEnvironment(std::vector<std::string> env) & {
206 env_ = std::move(env);
207 return *this;
208 }
209 Command SetEnvironment(std::vector<std::string> env) && {
210 return std::move(SetEnvironment(std::move(env)));
211 }
212
213 Command& AddEnvironmentVariable(const std::string& env_var,
214 const std::string& value) & {
215 return AddEnvironmentVariable(env_var + "=" + value);
216 }
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
223 Command& AddEnvironmentVariable(std::string env_var) & {
224 env_.emplace_back(std::move(env_var));
225 return *this;
226 }
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.
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 }
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>
255 std::stringstream ss;
256 BuildParameter(&ss, args...);
257 command_.push_back(ss.str());
258 return *this;
259 }
260 template <typename... 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>
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>
276 return std::move(AppendToLastParameter(std::forward<Args>(args)...));
277 }
278
279 // Redirects the standard IO of the command.
281 SharedFD shared_fd) &;
283 SharedFD shared_fd) &&;
285 Subprocess::StdIOChannel parent_channel) &;
287 Subprocess::StdIOChannel parent_channel) &&;
288
289 Command& SetWorkingDirectory(const std::string& path) &;
290 Command SetWorkingDirectory(const std::string& path) &&;
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.
300
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 std::string ToString() const;
308
309 // Generates the contents for a bash script that can be used to run this
310 // command. Note that this command must not require any file descriptors
311 // or stdio redirects as those would not be available when the bash script
312 // is run.
313 std::string AsBashScript(const std::string& redirected_stdio_path = "") const;
314
315 private:
316 friend std::ostream& operator<<(std::ostream& out, const Command& command);
317 std::optional<std::string> executable_; // When unset, use command_[0]
318 std::vector<std::string> command_;
319 std::vector<std::function<Result<void>()>> prerequisites_;
320 std::map<SharedFD, int> inherited_fds_{};
321 std::map<Subprocess::StdIOChannel, int> redirects_{};
322 std::vector<std::string> env_{};
325};
326std::ostream& operator<<(std::ostream& out, const Command& command);
327
340int Execute(const std::vector<std::string>& commands);
341int Execute(const std::vector<std::string>& commands,
342 const std::vector<std::string>& envs);
343
348Result<siginfo_t> Execute(const std::vector<std::string>& commands,
349 SubprocessOptions subprocess_options,
350 int wait_options);
351Result<siginfo_t> Execute(const std::vector<std::string>& commands,
352 const std::vector<std::string>& envs,
353 SubprocessOptions subprocess_options,
354 int wait_options);
355
356} // namespace cuttlefish
Definition: expected.h:86
Definition: subprocess.h:139
Command AddEnvironmentVariable(std::string env_var) &&
Definition: subprocess.h:227
~Command()
Definition: subprocess.cpp:309
Command & AddParameter(Args... args) &
Definition: subprocess.h:254
std::vector< std::string > command_
Definition: subprocess.h:318
std::map< SharedFD, int > inherited_fds_
Definition: subprocess.h:320
void BuildParameter(std::stringstream *stream, T t, Args... args)
Definition: subprocess.h:149
Command SetExecutable(std::string executable) &&
Definition: subprocess.h:175
Command & UnsetFromEnvironment(const std::string &env_var) &
Definition: subprocess.h:233
Command UnsetFromEnvironment(const std::string &env_var) &&
Definition: subprocess.h:244
friend std::ostream & operator<<(std::ostream &out, const Command &command)
Definition: subprocess.cpp:485
Command(std::string executable, SubprocessStopper stopper=KillSubprocess)
Definition: subprocess.cpp:301
Command & SetExecutable(std::string executable) &
Definition: subprocess.h:171
void BuildParameter(std::stringstream *stream, T t)
Definition: subprocess.h:143
std::string GetShortName() const
Definition: subprocess.h:301
Command & AddEnvironmentVariable(std::string env_var) &
Definition: subprocess.h:223
Command & AddEnvironmentVariable(const std::string &env_var, const std::string &value) &
Definition: subprocess.h:213
std::map< Subprocess::StdIOChannel, int > redirects_
Definition: subprocess.h:321
Command & AddPrerequisite(const std::function< Result< void >()> &prerequisite) &
Definition: subprocess.cpp:384
SharedFD working_directory_
Definition: subprocess.h:324
std::string AsBashScript(const std::string &redirected_stdio_path="") const
Definition: subprocess.cpp:511
std::vector< std::string > env_
Definition: subprocess.h:322
Command(const Command &)=delete
const std::string & Executable() const
Definition: subprocess.h:167
Subprocess Start(SubprocessOptions options=SubprocessOptions()) const
Definition: subprocess.cpp:396
std::string ToString() const
Definition: subprocess.cpp:501
Command & SetEnvironment(std::vector< std::string > env) &
Definition: subprocess.h:205
Command & operator=(const Command &)=delete
Command SetExecutableAndName(std::string name) &&
Definition: subprocess.h:191
std::vector< std::function< Result< void >()> > prerequisites_
Definition: subprocess.h:319
Command AppendToLastParameter(Args... args) &&
Definition: subprocess.h:275
Command SetEnvironment(std::vector< std::string > env) &&
Definition: subprocess.h:209
Command & SetName(std::string name) &
Definition: subprocess.h:179
Command AddEnvironmentVariable(const std::string &env_var, const std::string &value) &&
Definition: subprocess.h:217
Command SetStopper(SubprocessStopper stopper) &&
Definition: subprocess.h:199
Command & RedirectStdIO(Subprocess::StdIOChannel channel, SharedFD shared_fd) &
Definition: subprocess.cpp:333
Command & SetExecutableAndName(std::string name) &
Definition: subprocess.h:187
Command(Command &&)=default
Command SetName(std::string name) &&
Definition: subprocess.h:183
Command & AppendToLastParameter(Args... args) &
Definition: subprocess.h:267
Command AddParameter(Args... args) &&
Definition: subprocess.h:261
Command & SetWorkingDirectory(const std::string &path) &
Definition: subprocess.cpp:360
std::optional< std::string > executable_
Definition: subprocess.h:317
SubprocessStopper subprocess_stopper_
Definition: subprocess.h:323
Command & SetStopper(SubprocessStopper stopper) &
Definition: subprocess.h:195
Definition: shared_fd.h:129
Definition: subprocess.h:107
const std::string & Strace() const
Definition: subprocess.h:127
bool InGroup() const
Definition: subprocess.h:126
bool verbose_
Definition: subprocess.h:130
bool ExitWithParent() const
Definition: subprocess.h:125
SubprocessOptions & ExitWithParent(bool exit_with_parent) &
bool Verbose() const
Definition: subprocess.h:124
SubprocessOptions & SandboxArguments(std::vector< std::string >) &
bool in_group_
Definition: subprocess.h:132
SubprocessOptions ExitWithParent(bool exit_with_parent) &&
bool exit_with_parent_
Definition: subprocess.h:131
SubprocessOptions()
Definition: subprocess.h:109
std::string strace_
Definition: subprocess.h:133
SubprocessOptions SandboxArguments(std::vector< std::string >) &&
Definition: subprocess.h:66
bool started_
Definition: subprocess.h:103
std::atomic< pid_t > pid_
Definition: subprocess.h:102
SubprocessStopper stopper_
Definition: subprocess.h:104
Subprocess & operator=(const Subprocess &)=delete
Result< void > SendSignalToGroup(int signal)
Definition: subprocess.cpp:256
Result< void > SendSignal(int signal)
Definition: subprocess.cpp:251
Subprocess(pid_t pid, SubprocessStopper stopper=KillSubprocess)
Definition: subprocess.h:74
StdIOChannel
Definition: subprocess.h:68
Subprocess & operator=(Subprocess &&)
Definition: subprocess.cpp:171
Subprocess(const Subprocess &)=delete
bool Started() const
Definition: subprocess.h:89
int Wait()
Definition: subprocess.cpp:181
StopperResult Stop()
Definition: subprocess.h:91
pid_t pid() const
Definition: subprocess.h:90
#define CHECK(x)
Definition: logging.h:251
bool StartsWith(std::string_view s, std::string_view prefix)
Definition: strings.cpp:91
Definition: alloc_utils.cpp:23
StopperResult KillSubprocess(Subprocess *subprocess)
Definition: subprocess.cpp:261
StopperResult
Definition: subprocess.h:49
std::function< StopperResult(Subprocess *)> SubprocessStopper
Definition: subprocess.h:56
SubprocessStopper KillSubprocessFallback(std::function< StopperResult()> nice)
Definition: subprocess.cpp:284
int Execute(const std::vector< std::string > &commands, const std::vector< std::string > &envs)
Definition: subprocess.cpp:561
std::vector< std::string > ArgsToVec(int argc, char **argv)
Definition: flag_parser.cpp:346
std::ostream & operator<<(std::ostream &out, Arch arch)
Definition: architecture.cpp:67
std::unordered_map< std::string, std::string > EnvpToMap(char **envp)
Definition: subprocess.cpp:105
std::vector< std::string_view > Args
Definition: incremental.h:28
void verbose(const char *fmt,...)
Definition: util.cpp:69