/Users/deen/code/yugabyte-db/src/yb/util/subprocess.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | #ifndef YB_UTIL_SUBPROCESS_H |
33 | | #define YB_UTIL_SUBPROCESS_H |
34 | | |
35 | | #include <signal.h> |
36 | | #include <spawn.h> |
37 | | |
38 | | #include <map> |
39 | | #include <mutex> |
40 | | #include <string> |
41 | | #include <unordered_set> |
42 | | #include <vector> |
43 | | |
44 | | #include <glog/logging.h> |
45 | | |
46 | | #include "yb/gutil/macros.h" |
47 | | #include "yb/gutil/thread_annotations.h" |
48 | | |
49 | | #include "yb/util/enums.h" |
50 | | #include "yb/util/math_util.h" |
51 | | #include "yb/util/status.h" |
52 | | |
53 | | namespace yb { |
54 | | |
55 | | YB_DEFINE_ENUM(StdFdType, |
56 | | ((kIn, STDIN_FILENO)) |
57 | | ((kOut, STDOUT_FILENO)) |
58 | | ((kErr, STDERR_FILENO))); |
59 | | |
60 | | using StdFdTypes = EnumBitSet<StdFdType>; |
61 | | |
62 | | YB_DEFINE_ENUM(SubprocessState, (kNotStarted)(kRunning)(kExited)); |
63 | | YB_DEFINE_ENUM(SubprocessStreamMode, (kDisabled)(kShared)(kPiped)); |
64 | | |
65 | | // Wrapper around a spawned subprocess. |
66 | | // |
67 | | // program will be treated as an absolute path unless it begins with a dot or a |
68 | | // slash. |
69 | | // |
70 | | // This takes care of creating pipes to/from the subprocess and offers |
71 | | // basic functionality to wait on it or send signals. |
72 | | // By default, child process only has stdin captured and separate from the parent. |
73 | | // The stdout/stderr streams are shared with the parent by default. |
74 | | // |
75 | | // The process may only be started and waited on/killed once. |
76 | | // |
77 | | // Optionally, user may change parent/child stream sharing. Also, a user may disable |
78 | | // a subprocess stream. A user cannot do both. |
79 | | // |
80 | | // Note that, when the Subprocess object is destructed, the child process |
81 | | // will be forcibly SIGKILLed to avoid orphaning processes. |
82 | | class Subprocess { |
83 | | public: |
84 | | Subprocess(std::string program, std::vector<std::string> argv); |
85 | | ~Subprocess(); |
86 | | |
87 | | // Disable subprocess stream output. Must be called before subprocess starts. |
88 | | void DisableStderr(); |
89 | | void DisableStdout(); |
90 | | |
91 | | // Share a stream with parent. Must be called before subprocess starts. |
92 | | // Cannot set sharing at all if stream is disabled. |
93 | 0 | void ShareParentStdin() { SetFdShared(STDIN_FILENO, SubprocessStreamMode::kShared); } |
94 | 904 | void ShareParentStdout() { SetFdShared(STDOUT_FILENO, SubprocessStreamMode::kShared); } |
95 | 904 | void ShareParentStderr() { SetFdShared(STDERR_FILENO, SubprocessStreamMode::kShared); } |
96 | | |
97 | 0 | void PipeParentStdin() { SetFdShared(STDIN_FILENO, SubprocessStreamMode::kPiped); } |
98 | 1.07k | void PipeParentStdout() { SetFdShared(STDOUT_FILENO, SubprocessStreamMode::kPiped); } |
99 | 1.07k | void PipeParentStderr() { SetFdShared(STDERR_FILENO, SubprocessStreamMode::kPiped); } |
100 | | |
101 | | // Marks a non-standard file descriptor which should not be closed after |
102 | | // forking the child process. |
103 | | void InheritNonstandardFd(int fd); |
104 | | |
105 | | // Start the subprocess. Can only be called once. |
106 | | // |
107 | | // This returns a bad Status if the fork() fails. However, |
108 | | // note that if the executable path was incorrect such that |
109 | | // exec() fails, this will still return Status::OK. You must |
110 | | // use Wait() to check for failure. |
111 | | CHECKED_STATUS Start(); |
112 | | |
113 | | // Wait for the subprocess to exit. The return value is the same as |
114 | | // that of the waitpid() syscall. Only call after starting. |
115 | | // |
116 | | // NOTE: unlike the standard wait(2) call, this may be called multiple |
117 | | // times. If the process has exited, it will repeatedly return the same |
118 | | // exit code. |
119 | | // |
120 | | // The integer pointed by ret (ret must be non-NULL) is set to the "status" value as described |
121 | | // by the waitpid documentation (https://linux.die.net/man/2/waitpid), paraphrasing below. |
122 | | // |
123 | | // If ret is not NULL, wait() and waitpid() store status information in the int to which it |
124 | | // points. This integer can be inspected with the following macros (which take the integer |
125 | | // itself as an argument, not a pointer to it, as is done in wait() and waitpid()!): |
126 | | // WCONTINUED, WCOREDUMP, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, |
127 | | // WNOHANG, WSTOPSIG, WTERMSIG, WUNTRACED. |
128 | | CHECKED_STATUS Wait(int* ret); |
129 | | |
130 | | Result<int> Wait(); |
131 | | |
132 | | // Like the above, but does not block. This returns Status::TimedOut |
133 | | // immediately if the child has not exited. Otherwise returns Status::OK |
134 | | // and sets *ret. Only call after starting. |
135 | | // |
136 | | // NOTE: unlike the standard wait(2) call, this may be called multiple |
137 | | // times. If the process has exited, it will repeatedly return the same |
138 | | // exit code. |
139 | | CHECKED_STATUS WaitNoBlock(int* ret); |
140 | | |
141 | | // Send a signal to the subprocess. |
142 | | // Note that this does not reap the process -- you must still Wait() |
143 | | // in order to reap it. Only call after starting. |
144 | | CHECKED_STATUS Kill(int signal); |
145 | | |
146 | | // Similar to Kill, but does not enforce that the process must be running. |
147 | | CHECKED_STATUS KillNoCheckIfRunning(int signal); |
148 | | |
149 | | // Returns true if the process is running. |
150 | | bool IsRunning() const; |
151 | | |
152 | | // Helper method that creates a Subprocess, issues a Start() then a Wait(). |
153 | | // Expects a blank-separated list of arguments, with the first being the |
154 | | // full path to the executable. |
155 | | // The returned Status will only be OK if all steps were successful and |
156 | | // the return code was 0. |
157 | | static CHECKED_STATUS Call(const std::string& arg_str); |
158 | | |
159 | | // Same as above, but accepts a vector that includes the path to the |
160 | | // executable as argv[0] and the arguments to the program in argv[1..n]. |
161 | | static CHECKED_STATUS Call(const std::vector<std::string>& argv); |
162 | | |
163 | | // Same as above, but collects the output from the child process stdout into |
164 | | // the output parameter. |
165 | | // If read_stderr is set to true, stderr is collected instead. |
166 | | static CHECKED_STATUS Call( |
167 | | const std::vector<std::string>& argv, |
168 | | std::string* output, StdFdTypes read_fds = StdFdTypes{StdFdType::kOut}); |
169 | | |
170 | | // Return the pipe fd to the child's standard stream. |
171 | | // Stream should not be disabled or shared. |
172 | 0 | int to_child_stdin_fd() const { return CheckAndOffer(STDIN_FILENO); } |
173 | 3 | int from_child_stdout_fd() const { return CheckAndOffer(STDOUT_FILENO); } |
174 | 2 | int from_child_stderr_fd() const { return CheckAndOffer(STDERR_FILENO); } |
175 | | |
176 | | // Release control of the file descriptor for the child's stream, only if piped. |
177 | | // Writes to this FD show up on stdin in the subprocess |
178 | 7 | int ReleaseChildStdinFd() { return ReleaseChildFd(STDIN_FILENO ); } |
179 | | // Reads from this FD come from stdout of the subprocess |
180 | 1.07k | int ReleaseChildStdoutFd() { return ReleaseChildFd(STDOUT_FILENO); } |
181 | | // Reads from this FD come from stderr of the subprocess |
182 | 1.07k | int ReleaseChildStderrFd() { return ReleaseChildFd(STDERR_FILENO); } |
183 | | |
184 | | pid_t pid() const; |
185 | | |
186 | | void SetEnv(const std::string& key, const std::string& value); |
187 | | |
188 | | // Issues Start() then Wait() and collects the output from the child process |
189 | | // (stdout or stderr) into the output parameter. |
190 | | CHECKED_STATUS Call(std::string* output, StdFdTypes read_fds = StdFdTypes{StdFdType::kOut}); |
191 | | |
192 | | // Writes pid to cgroup specified by path |
193 | | void AddPIDToCGroup(const string& path, pid_t pid); |
194 | | |
195 | | private: |
196 | | |
197 | | struct ChildPipes { |
198 | | int child_stdin[2] = {-1, -1}; |
199 | | int child_stdout[2] = {-1, -1}; |
200 | | int child_stderr[2] = {-1, -1}; |
201 | | }; |
202 | | |
203 | | CHECKED_STATUS StartWithForkExec() REQUIRES(state_lock_); |
204 | | CHECKED_STATUS StartWithPosixSpawn() REQUIRES(state_lock_); |
205 | | |
206 | | void SetFdShared(int stdfd, SubprocessStreamMode mode); |
207 | | int CheckAndOffer(int stdfd) const; |
208 | | int ReleaseChildFd(int stdfd); |
209 | | CHECKED_STATUS DoWait(int* ret, int options); |
210 | | SubprocessState state() const; |
211 | | CHECKED_STATUS KillInternal(int signal, bool must_be_running); |
212 | | |
213 | | // Combine the existing environment with the overrides from env_, and return it as a vector |
214 | | // of name=value strings and a pointer array terminated with a null, referring to the vector, |
215 | | // suitable for use with standard C library functions. |
216 | | std::pair<std::vector<std::string>, std::vector<char*>> GetCombinedEnv(); |
217 | | |
218 | | Result<std::vector<char*>> GetArgvPtrs() REQUIRES(state_lock_); |
219 | | Result<ChildPipes> CreateChildPipes() REQUIRES(state_lock_); |
220 | | |
221 | | Status ConfigureFileActionsForPosixSpawn( |
222 | | posix_spawn_file_actions_t* file_actions, const ChildPipes& child_pipes) |
223 | | REQUIRES(state_lock_); |
224 | | |
225 | | Status ConfigureOutputStreamActionForPosixSpawn( |
226 | | posix_spawn_file_actions_t* file_actions, int out_stream_fd, int child_write_fd) |
227 | | REQUIRES(state_lock_); |
228 | | |
229 | | // Finds all open file descriptors other than stdin/stdout/stderr and not included in |
230 | | // ns_fds_inherited_, and registers them in file_actions to be closed during posix_spawn. |
231 | | // Returns the list of file descriptors to be closed. |
232 | | Result<std::vector<int>> CloseFileDescriptorsForPosixSpawn( |
233 | | posix_spawn_file_actions_t* file_actions) REQUIRES(state_lock_); |
234 | | |
235 | | void FinalizeParentSideOfPipes(const ChildPipes& child_pipes) REQUIRES(state_lock_); |
236 | | |
237 | | void ConfigureOutputStreamAfterFork(int out_stream_fd, int child_write_fd); |
238 | | |
239 | | // ---------------------------------------------------------------------------------------------- |
240 | | // Fields |
241 | | // ---------------------------------------------------------------------------------------------- |
242 | | |
243 | | std::string program_; |
244 | | std::vector<std::string> argv_; |
245 | | |
246 | | mutable std::mutex state_lock_; |
247 | | SubprocessState state_; |
248 | | pid_t child_pid_; |
249 | | SubprocessStreamMode fd_state_[3]; |
250 | | int child_fds_[3]; |
251 | | |
252 | | // The cached exit result code if Wait() has been called. |
253 | | // Only valid if state_ == kExited. |
254 | | int cached_rc_; |
255 | | |
256 | | std::map<std::string, std::string> env_; |
257 | | |
258 | | // List of non-standard file descriptors which should be inherited by the |
259 | | // child process. |
260 | | |
261 | | std::unordered_set<int> ns_fds_inherited_; |
262 | | |
263 | | DISALLOW_COPY_AND_ASSIGN(Subprocess); |
264 | | }; |
265 | | |
266 | | } // namespace yb |
267 | | #endif /* YB_UTIL_SUBPROCESS_H */ |