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