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