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