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 20 #include <functional> 21 #include <map> 22 #include <sstream> 23 #include <string> 24 #include <unordered_set> 25 #include <vector> 26 27 #include <android-base/logging.h> 28 29 #include <common/libs/fs/shared_fd.h> 30 31 namespace cuttlefish { 32 class Command; 33 class Subprocess; 34 class SubprocessOptions; 35 using SubprocessStopper = std::function<bool(Subprocess*)>; 36 // Kills a process by sending it the SIGKILL signal. 37 bool KillSubprocess(Subprocess* subprocess); 38 39 // Keeps track of a running (sub)process. Allows to wait for its completion. 40 // It's an error to wait twice for the same subprocess. 41 class Subprocess { 42 public: 43 enum class StdIOChannel { 44 kStdIn = 0, 45 kStdOut = 1, 46 kStdErr = 2, 47 }; 48 49 Subprocess(pid_t pid, SubprocessStopper stopper = KillSubprocess) pid_(pid)50 : pid_(pid), 51 started_(pid > 0), 52 stopper_(stopper) {} 53 // The default implementation won't do because we need to reset the pid of the 54 // moved object. 55 Subprocess(Subprocess&&); 56 ~Subprocess() = default; 57 Subprocess& operator=(Subprocess&&); 58 // Waits for the subprocess to complete. Returns zero if completed 59 // successfully, non-zero otherwise. 60 int Wait(); 61 // Same as waitpid(2) 62 pid_t Wait(int* wstatus, int options); 63 // Whether the command started successfully. It only says whether the call to 64 // fork() succeeded or not, it says nothing about exec or successful 65 // completion of the command, that's what Wait is for. Started()66 bool Started() const { return started_; } pid()67 pid_t pid() const { return pid_; } Stop()68 bool Stop() { return stopper_(this); } 69 70 private: 71 // Copy is disabled to avoid waiting twice for the same pid (the first wait 72 // frees the pid, which allows the kernel to reuse it so we may end up waiting 73 // for the wrong process) 74 Subprocess(const Subprocess&) = delete; 75 Subprocess& operator=(const Subprocess&) = delete; 76 pid_t pid_ = -1; 77 bool started_ = false; 78 SubprocessStopper stopper_; 79 }; 80 81 class SubprocessOptions { 82 bool verbose_; 83 bool exit_with_parent_; 84 bool in_group_; 85 public: SubprocessOptions()86 SubprocessOptions() : verbose_(true), exit_with_parent_(true) {} 87 Verbose(bool verbose)88 void Verbose(bool verbose) { 89 verbose_ = verbose; 90 } ExitWithParent(bool exit_with_parent)91 void ExitWithParent(bool exit_with_parent) { 92 exit_with_parent_ = exit_with_parent; 93 } 94 // The subprocess runs as head of its own process group. InGroup(bool in_group)95 void InGroup(bool in_group) { 96 in_group_ = in_group; 97 } 98 Verbose()99 bool Verbose() const { return verbose_; } ExitWithParent()100 bool ExitWithParent() const { return exit_with_parent_; } InGroup()101 bool InGroup() const { return in_group_; } 102 }; 103 104 // An executable command. Multiple subprocesses can be started from the same 105 // command object. This class owns any file descriptors that the subprocess 106 // should inherit. 107 class Command { 108 private: 109 template <typename T> 110 // For every type other than SharedFD (for which there is a specialisation) BuildParameter(std::stringstream * stream,T t)111 bool BuildParameter(std::stringstream* stream, T t) { 112 *stream << t; 113 return true; 114 } 115 // Special treatment for SharedFD 116 bool BuildParameter(std::stringstream* stream, SharedFD shared_fd); 117 template <typename T, typename... Args> BuildParameter(std::stringstream * stream,T t,Args...args)118 bool BuildParameter(std::stringstream* stream, T t, Args... args) { 119 return BuildParameter(stream, t) && BuildParameter(stream, args...); 120 } 121 122 public: 123 // Constructs a command object from the path to an executable binary and an 124 // optional subprocess stopper. When not provided, stopper defaults to sending 125 // SIGKILL to the subprocess. 126 Command(const std::string& executable, 127 SubprocessStopper stopper = KillSubprocess) subprocess_stopper_(stopper)128 : subprocess_stopper_(stopper) { 129 command_.push_back(executable); 130 } 131 Command(Command&&) = default; 132 // The default copy constructor is unsafe because it would mean multiple 133 // closing of the inherited file descriptors. If needed it can be implemented 134 // using dup(2) 135 Command(const Command&) = delete; 136 Command& operator=(const Command&) = delete; 137 ~Command(); 138 139 // Specify the environment for the subprocesses to be started. By default 140 // subprocesses inherit the parent's environment. SetEnvironment(const std::vector<std::string> & env)141 void SetEnvironment(const std::vector<std::string>& env) { 142 use_parent_env_ = false; 143 env_ = env; 144 } 145 146 // Specify environment variables to be unset from the parent's environment 147 // for the subprocesses to be started. UnsetFromEnvironment(const std::vector<std::string> & env)148 void UnsetFromEnvironment(const std::vector<std::string>& env) { 149 use_parent_env_ = true; 150 std::copy(env.cbegin(), env.cend(), std::inserter(unenv_, unenv_.end())); 151 } 152 153 // Adds a single parameter to the command. All arguments are concatenated into 154 // a single string to form a parameter. If one of those arguments is a 155 // SharedFD a duplicate of it will be used and won't be closed until the 156 // object is destroyed. To add multiple parameters to the command the function 157 // must be called multiple times, one per parameter. 158 template <typename... Args> AddParameter(Args...args)159 bool AddParameter(Args... args) { 160 std::stringstream ss; 161 if (BuildParameter(&ss, args...)) { 162 command_.push_back(ss.str()); 163 return true; 164 } 165 return false; 166 } 167 // Similar to AddParameter, except the args are appended to the last (most 168 // recently-added) parameter in the command. 169 template <typename... Args> AppendToLastParameter(Args...args)170 bool AppendToLastParameter(Args... args) { 171 if (command_.empty()) { 172 LOG(ERROR) << "There is no parameter to append to."; 173 return false; 174 } 175 std::stringstream ss; 176 if (BuildParameter(&ss, args...)) { 177 command_[command_.size()-1] += ss.str(); 178 return true; 179 } 180 return false; 181 } 182 183 // Redirects the standard IO of the command. 184 bool RedirectStdIO(Subprocess::StdIOChannel channel, SharedFD shared_fd); 185 bool RedirectStdIO(Subprocess::StdIOChannel subprocess_channel, 186 Subprocess::StdIOChannel parent_channel); 187 188 // Starts execution of the command. This method can be called multiple times, 189 // effectively staring multiple (possibly concurrent) instances. 190 Subprocess Start(SubprocessOptions options = SubprocessOptions()) const; 191 GetShortName()192 std::string GetShortName() const { 193 // This is safe because the constructor guarantees the name of the binary to 194 // be at index 0 on the vector 195 return command_[0]; 196 } 197 198 private: 199 std::vector<std::string> command_; 200 std::map<SharedFD, int> inherited_fds_{}; 201 std::map<Subprocess::StdIOChannel, int> redirects_{}; 202 bool use_parent_env_ = true; 203 std::vector<std::string> env_{}; 204 std::unordered_set<std::string> unenv_{}; 205 SubprocessStopper subprocess_stopper_; 206 }; 207 208 /* 209 * Consumes a Command and runs it, optionally managing the stdio channels. 210 * 211 * If `stdin` is set, the subprocess stdin will be pipe providing its contents. 212 * If `stdout` is set, the subprocess stdout will be captured and saved to it. 213 * If `stderr` is set, the subprocess stderr will be captured and saved to it. 214 * 215 * If `command` exits normally, the lower 8 bits of the return code will be 216 * returned in a value between 0 and 255. 217 * If some setup fails, `command` fails to start, or `command` exits due to a 218 * signal, the return value will be negative. 219 */ 220 int RunWithManagedStdio(Command&& command, const std::string* stdin, 221 std::string* stdout, std::string* stderr, 222 SubprocessOptions options = SubprocessOptions()); 223 224 // Convenience wrapper around Command and Subprocess class, allows to easily 225 // execute a command and wait for it to complete. The version without the env 226 // parameter starts the command with the same environment as the parent. Returns 227 // zero if the command completed successfully, non zero otherwise. 228 int execute(const std::vector<std::string>& command, 229 const std::vector<std::string>& env); 230 int execute(const std::vector<std::string>& command); 231 232 } // namespace cuttlefish 233