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 <map> 21 #include <string> 22 #include <sstream> 23 #include <vector> 24 25 #include <common/libs/fs/shared_fd.h> 26 27 namespace cvd { 28 // Keeps track of a running (sub)process. Allows to wait for its completion. 29 // It's an error to wait twice for the same subprocess. 30 class Subprocess { 31 public: 32 enum class StdIOChannel { 33 kStdIn = 0, 34 kStdOut = 1, 35 kStdErr = 2, 36 }; 37 Subprocess(pid_t pid,SharedFD control)38 Subprocess(pid_t pid, SharedFD control) 39 : pid_(pid), started_(pid > 0), control_socket_(control) {} 40 // The default implementation won't do because we need to reset the pid of the 41 // moved object. 42 Subprocess(Subprocess&&); 43 ~Subprocess() = default; 44 Subprocess& operator=(Subprocess&&); 45 // Waits for the subprocess to complete. Returns zero if completed 46 // successfully, non-zero otherwise. 47 int Wait(); 48 // Same as waitpid(2) 49 pid_t Wait(int* wstatus, int options); 50 // Whether the command started successfully. It only says whether the call to 51 // fork() succeeded or not, it says nothing about exec or successful 52 // completion of the command, that's what Wait is for. Started()53 bool Started() const {return started_;} control_socket()54 SharedFD control_socket() { 55 return control_socket_; 56 } 57 58 private: 59 // Copy is disabled to avoid waiting twice for the same pid (the first wait 60 // frees the pid, which allows the kernel to reuse it so we may end up waiting 61 // for the wrong process) 62 Subprocess(const Subprocess&) = delete; 63 Subprocess& operator=(const Subprocess&) = delete; 64 pid_t pid_ = -1; 65 bool started_ = false; 66 SharedFD control_socket_; 67 }; 68 69 // An executable command. Multiple subprocesses can be started from the same 70 // command object. This class owns any file descriptors that the subprocess 71 // should inherit. 72 class Command { 73 private: 74 template<typename T> 75 // For every type other than SharedFD (for which there is a specialisation) BuildParameter(std::stringstream * stream,T t)76 bool BuildParameter(std::stringstream* stream, T t) { 77 *stream << t; 78 return true; 79 } 80 // Special treatment for SharedFD 81 bool BuildParameter(std::stringstream* stream, SharedFD shared_fd); 82 template<typename T, typename...Args> BuildParameter(std::stringstream * stream,T t,Args...args)83 bool BuildParameter(std::stringstream* stream, T t, Args...args) { 84 return BuildParameter(stream, t) && 85 BuildParameter(stream, args...); 86 } 87 public: Command(const std::string & executable)88 Command(const std::string& executable) { 89 command_.push_back(executable); 90 } 91 Command(Command&&) = default; 92 // The default copy constructor is unsafe because it would mean multiple 93 // closing of the inherited file descriptors. If needed it can be implemented 94 // using dup(2) 95 Command(const Command&) = delete; 96 Command& operator=(const Command&) = delete; 97 ~Command(); 98 99 // Specify the environment for the subprocesses to be started. By default 100 // subprocesses inherit the parent's environment. SetEnvironment(const std::vector<std::string> & env)101 void SetEnvironment(const std::vector<std::string>& env) { 102 use_parent_env_ = false; 103 env_ = env; 104 } 105 // Adds a single parameter to the command. All arguments are concatenated into 106 // a single string to form a parameter. If one of those arguments is a 107 // SharedFD a duplicate of it will be used and won't be closed until the 108 // object is destroyed. To add multiple parameters to the command the function 109 // must be called multiple times, one per parameter. 110 template<typename... Args> AddParameter(Args...args)111 bool AddParameter(Args... args) { 112 std::stringstream ss; 113 if (BuildParameter(&ss, args...)) { 114 command_.push_back(ss.str()); 115 return true; 116 } 117 return false; 118 } 119 120 // Redirects the standard IO of the command. 121 bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd); 122 123 // Starts execution of the command. This method can be called multiple times, 124 // effectively staring multiple (possibly concurrent) instances. If 125 // with_control_socket is true the returned Subprocess instance will have a 126 // sharedFD that enables communication with the child process. 127 Subprocess Start(bool with_control_socket = false) const; 128 GetShortName()129 std::string GetShortName() const { 130 // This is safe because the constructor guarantees the name of the binary to 131 // be at index 0 on the vector 132 return command_[0]; 133 } 134 private: 135 std::vector<std::string> command_; 136 std::map<cvd::SharedFD, int> inherited_fds_{}; 137 std::map<Subprocess::StdIOChannel, int> redirects_{}; 138 bool use_parent_env_ = true; 139 std::vector<std::string> env_{}; 140 }; 141 142 // Convenience wrapper around Command and Subprocess class, allows to easily 143 // execute a command and wait for it to complete. The version without the env 144 // parameter starts the command with the same environment as the parent. Returns 145 // zero if the command completed successfully, non zero otherwise. 146 int execute(const std::vector<std::string>& command, 147 const std::vector<std::string>& env); 148 int execute(const std::vector<std::string>& command); 149 150 } // namespace cvd 151