1 /* 2 * Copyright (C) 2020 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 17 #ifndef INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 19 20 #include "perfetto/base/build_config.h" 21 22 // This is a #if as opposite to a GN condition, because GN conditions aren't propagated when 23 // translating to Bazel or other build systems, as they get resolved at translation time. Without 24 // this, the Bazel build breaks on Windows. 25 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ 26 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ 27 PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX) 28 #define PERFETTO_HAS_SUBPROCESS() 1 29 #else 30 #define PERFETTO_HAS_SUBPROCESS() 0 31 #endif 32 33 #include <functional> 34 #include <initializer_list> 35 #include <string> 36 #include <thread> 37 #include <vector> 38 39 #include "perfetto/base/logging.h" 40 #include "perfetto/base/proc_utils.h" 41 #include "perfetto/ext/base/pipe.h" 42 #include "perfetto/ext/base/scoped_file.h" 43 44 namespace perfetto { 45 namespace base { 46 47 // Handles creation and lifecycle management of subprocesses, taking care of 48 // all subtleties involved in handling processes on UNIX. 49 // This class allows to deal with macro two use-cases: 50 // 1) fork() + exec() equivalent: for spawning a brand new process image. 51 // This happens when |args.exec_cmd| is not empty. 52 // This is safe to use even in a multi-threaded environment. 53 // 2) fork(): for spawning a process and running a function. 54 // This happens when |args.entrypoint_for_testing| is not empty. 55 // This is intended only for tests as it is extremely subtle. 56 // This mode must be used with extreme care. Before the entrypoint is 57 // invoked all file descriptors other than stdin/out/err and the ones 58 // specified in |args.preserve_fds| will be closed, to avoid each process 59 // retaining a dupe of other subprocesses pipes. This however means that 60 // any non trivial calls (including logging) must be avoided as they might 61 // refer to FDs that are now closed. The entrypoint should really be used 62 // just to signal a pipe or similar for synchronizing sequencing in tests. 63 64 // 65 // This class allows to control stdin/out/err pipe redirection and takes care 66 // of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar 67 // fashion of python's subprocess.Communicate() 68 // stdin: is always piped and closed once the |args.input| buffer is written. 69 // stdout/err can be either: 70 // - dup()ed onto the parent process stdout/err. 71 // - redirected onto /dev/null. 72 // - piped onto a buffer (see output() method). There is only one output 73 // buffer in total. If both stdout and stderr are set to kBuffer mode, they 74 // will be merged onto the same. There doesn't seem any use case where they 75 // are needed distinctly. 76 // 77 // Some caveats worth mentioning: 78 // - It always waitpid()s, to avoid leaving zombies around. If the process is 79 // not terminated by the time the destructor is reached, the dtor will 80 // send a SIGKILL and wait for the termination. 81 // - After fork()-ing it will close all file descriptors, preserving only 82 // stdin/out/err and the fds listed in |args.preserve_fds|. 83 // - On Linux/Android, the child process will be SIGKILL-ed if the calling 84 // thread exists, even if the Subprocess is std::move()-d onto another thread. 85 // This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that 86 // child processes are leaked in the case of a crash of the parent (frequent 87 // in tests). However, the child process might still be leaked if execing 88 // a setuid/setgid binary (see man 2 prctl). 89 // 90 // Usage: 91 // base::Subprocess p({"/bin/cat", "-"}); 92 // (or equivalently: 93 // base::Subprocess p; 94 // p.args.exec_cmd.push_back("/bin/cat"); 95 // p.args.exec_cmd.push_back("-"); 96 // ) 97 // p.args.stdout_mode = base::Subprocess::kBuffer; 98 // p.args.stderr_mode = base::Subprocess::kInherit; 99 // p.args.input = "stdin contents"; 100 // p.Call(); 101 // (or equivalently: 102 // p.Start(); 103 // p.Wait(); 104 // ) 105 // EXPECT_EQ(p.status(), base::Subprocess::kExited); 106 // EXPECT_EQ(p.returncode(), 0); 107 class Subprocess { 108 public: 109 enum Status { 110 kNotStarted = 0, // Before calling Start() or Call(). 111 kRunning, // After calling Start(), before Wait(). 112 kExited, // The subprocess exited (either succesully or not). 113 kKilledBySignal, // The subprocess has been killed by a signal. 114 }; 115 116 enum OutputMode { 117 kInherit = 0, // Inherit's the caller process stdout/stderr. 118 kDevNull, // dup() onto /dev/null 119 kBuffer // dup() onto a pipe and move it into the output() buffer. 120 }; 121 122 // Input arguments for configuring the subprocess behavior. 123 struct Args { exec_cmdArgs124 Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {} 125 Args(Args&&) noexcept; 126 Args& operator=(Args&&); 127 // If non-empty this will cause an exec() when Start()/Call() are called. 128 std::vector<std::string> exec_cmd; 129 130 // If non-empty, it changes the argv[0] argument passed to exec. If 131 // unset, argv[0] == exec_cmd[0]. This is to handle cases like: 132 // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override". 133 std::string argv0_override; 134 135 // If non-empty this will be invoked on the fork()-ed child process, after 136 // stdin/out/err has been redirected and all other file descriptor are 137 // closed. 138 // It is valid to specify both |exec_cmd| AND |entrypoint_for_testing|. 139 // In this case |entrypoint_for_testing| will be invoked just before the 140 // exec() call, but after having closed all fds % stdin/out/err. 141 // This is for synchronization barriers in tests. 142 std::function<void()> entrypoint_for_testing; 143 144 // If non-empty, replaces the environment passed to exec(). 145 std::vector<std::string> env; 146 147 // The file descriptors in this list will not be closed. 148 std::vector<int> preserve_fds; 149 150 // The data to push in the child process stdin. 151 std::string input; 152 153 OutputMode stdout_mode = kInherit; 154 OutputMode stderr_mode = kInherit; 155 156 // Returns " ".join(exec_cmd), quoting arguments. 157 std::string GetCmdString() const; 158 }; 159 160 explicit Subprocess(std::initializer_list<std::string> exec_cmd = {}); 161 Subprocess(Subprocess&&) noexcept; 162 Subprocess& operator=(Subprocess&&); 163 ~Subprocess(); // It will KillAndWaitForTermination() if still alive. 164 165 // Starts the subprocess but doesn't wait for its termination. The caller 166 // is expected to either call Wait() or Poll() after this call. 167 void Start(); 168 169 // Wait for process termination. Can be called more than once. 170 // Args: 171 // |timeout_ms| = 0: wait indefinitely. 172 // |timeout_ms| > 0: wait for at most |timeout_ms|. 173 // Returns: 174 // True: The process terminated. See status() and returncode(). 175 // False: Timeout reached, the process is still running. In this case the 176 // process will be left in the kRunning state. 177 bool Wait(int timeout_ms = 0); 178 179 // Equivalent of Start() + Wait(); 180 // Returns true if the process exited cleanly with return code 0. False in 181 // any othe case. 182 bool Call(int timeout_ms = 0); 183 184 Status Poll(); 185 186 // Sends a SIGKILL and wait to see the process termination. 187 void KillAndWaitForTermination(); 188 pid()189 PlatformProcessId pid() const { return pid_; } status()190 Status status() const { return status_; } returncode()191 int returncode() const { return returncode_; } 192 193 // This contains both stdout and stderr (if the corresponding _mode == 194 // kBuffer). It's non-const so the caller can std::move() it. output()195 std::string& output() { return output_; } 196 197 Args args; 198 199 private: 200 Subprocess(const Subprocess&) = delete; 201 Subprocess& operator=(const Subprocess&) = delete; 202 void TryPushStdin(); 203 void TryReadStdoutAndErr(); 204 void TryReadExitStatus(); 205 void KillAtMostOnce(); 206 bool PollInternal(int poll_timeout_ms); 207 208 base::Pipe stdin_pipe_; 209 base::Pipe stdouterr_pipe_; 210 base::Pipe exit_status_pipe_; 211 PlatformProcessId pid_; 212 size_t input_written_ = 0; 213 Status status_ = kNotStarted; 214 int returncode_ = -1; 215 std::string output_; // Stdin+stderr. Only when kBuffer. 216 std::thread waitpid_thread_; 217 }; 218 219 } // namespace base 220 } // namespace perfetto 221 222 #endif // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_ 223