YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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 */