1    	#include "SubProcess.h"
2    	
3    	#if defined(__FreeBSD__) || defined(__APPLE__)
4    	#include <sys/types.h>
5    	#include <signal.h>
6    	#endif
7    	#include <stdarg.h>
8    	#include <fcntl.h>
9    	#include <unistd.h>
10   	#include <iostream>
11   	
12   	#include "common/errno.h"
13   	#include "include/ceph_assert.h"
14   	#include "include/compat.h"
15   	
16   	SubProcess::SubProcess(const char *cmd_, std_fd_op stdin_op_, std_fd_op stdout_op_, std_fd_op stderr_op_) :
17   	  cmd(cmd_),
18   	  cmd_args(),
19   	  stdin_op(stdin_op_),
20   	  stdout_op(stdout_op_),
21   	  stderr_op(stderr_op_),
22   	  stdin_pipe_out_fd(-1),
23   	  stdout_pipe_in_fd(-1),
24   	  stderr_pipe_in_fd(-1),
25   	  pid(-1),
26   	  errstr() {
27   	}
28   	
(1) Event exn_spec_violation: An exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate().
Also see events: [fun_call_w_exception]
29   	SubProcess::~SubProcess() {
30   	  ceph_assert(!is_spawned());
31   	  ceph_assert(stdin_pipe_out_fd == -1);
(2) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
Also see events: [exn_spec_violation]
32   	  ceph_assert(stdout_pipe_in_fd == -1);
33   	  ceph_assert(stderr_pipe_in_fd == -1);
34   	}
35   	
36   	void SubProcess::add_cmd_args(const char *arg, ...) {
37   	  ceph_assert(!is_spawned());
38   	
39   	  va_list ap;
40   	  va_start(ap, arg);
41   	  const char *p = arg;
42   	  do {
43   	    add_cmd_arg(p);
44   	    p = va_arg(ap, const char*);
45   	  } while (p != NULL);
46   	  va_end(ap);
47   	}
48   	
49   	void SubProcess::add_cmd_arg(const char *arg) {
50   	  ceph_assert(!is_spawned());
51   	
52   	  cmd_args.push_back(arg);
53   	}
54   	
55   	int SubProcess::get_stdin() const {
56   	  ceph_assert(is_spawned());
57   	  ceph_assert(stdin_op == PIPE);
58   	
59   	  return stdin_pipe_out_fd;
60   	}
61   	
62   	int SubProcess::get_stdout() const {
63   	  ceph_assert(is_spawned());
64   	  ceph_assert(stdout_op == PIPE);
65   	
66   	  return stdout_pipe_in_fd;
67   	}
68   	
69   	int SubProcess::get_stderr() const {
70   	  ceph_assert(is_spawned());
71   	  ceph_assert(stderr_op == PIPE);
72   	
73   	  return stderr_pipe_in_fd;
74   	}
75   	
76   	void SubProcess::close(int &fd) {
77   	  if (fd == -1)
78   	    return;
79   	
80   	  ::close(fd);
81   	  fd = -1;
82   	}
83   	
84   	void SubProcess::close_stdin() {
85   	  ceph_assert(is_spawned());
86   	  ceph_assert(stdin_op == PIPE);
87   	
88   	  close(stdin_pipe_out_fd);
89   	}
90   	
91   	void SubProcess::close_stdout() {
92   	  ceph_assert(is_spawned());
93   	  ceph_assert(stdout_op == PIPE);
94   	
95   	  close(stdout_pipe_in_fd);
96   	}
97   	
98   	void SubProcess::close_stderr() {
99   	  ceph_assert(is_spawned());
100  	  ceph_assert(stderr_op == PIPE);
101  	
102  	  close(stderr_pipe_in_fd);
103  	}
104  	
105  	void SubProcess::kill(int signo) const {
106  	  ceph_assert(is_spawned());
107  	
108  	  int ret = ::kill(pid, signo);
109  	  ceph_assert(ret == 0);
110  	}
111  	
112  	const std::string SubProcess::err() const {
113  	  return errstr.str();
114  	}
115  	
116  	class fd_buf : public std::streambuf {
117  	  int fd;
118  	public:
119  	  fd_buf (int fd) : fd(fd)
120  	  {}
121  	protected:
122  	  int_type overflow (int_type c) override {
123  	    if (c == EOF) return EOF;
124  	    char buf = c;
125  	    if (write (fd, &buf, 1) != 1) {
126  	      return EOF;
127  	    }
128  	    return c;
129  	  }
130  	  std::streamsize xsputn (const char* s, std::streamsize count) override {
131  	    return write(fd, s, count);
132  	  }
133  	};
134  	
135  	int SubProcess::spawn() {
136  	  ceph_assert(!is_spawned());
137  	  ceph_assert(stdin_pipe_out_fd == -1);
138  	  ceph_assert(stdout_pipe_in_fd == -1);
139  	  ceph_assert(stderr_pipe_in_fd == -1);
140  	
141  	  enum { IN = 0, OUT = 1 };
142  	
143  	  int ipipe[2], opipe[2], epipe[2];
144  	
145  	  ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = epipe[0] = epipe[1] = -1;
146  	
147  	  int ret = 0;
148  	
149  	  if ((stdin_op == PIPE  && pipe_cloexec(ipipe, 0) == -1) ||
150  	      (stdout_op == PIPE && pipe_cloexec(opipe, 0) == -1) ||
151  	      (stderr_op == PIPE && pipe_cloexec(epipe, 0) == -1)) {
152  	    ret = -errno;
153  	    errstr << "pipe failed: " << cpp_strerror(errno);
154  	    goto fail;
155  	  }
156  	
157  	  pid = fork();
158  	
159  	  if (pid > 0) { // Parent
160  	    stdin_pipe_out_fd = ipipe[OUT]; close(ipipe[IN ]);
161  	    stdout_pipe_in_fd = opipe[IN ]; close(opipe[OUT]);
162  	    stderr_pipe_in_fd = epipe[IN ]; close(epipe[OUT]);
163  	    return 0;
164  	  }
165  	
166  	  if (pid == 0) { // Child
167  	    close(ipipe[OUT]);
168  	    close(opipe[IN ]);
169  	    close(epipe[IN ]);
170  	
171  	    if (ipipe[IN] >= 0) {
172  	      if (ipipe[IN] == STDIN_FILENO) {
173  	        ::fcntl(STDIN_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */
174  	      } else {
175  	        ::dup2(ipipe[IN], STDIN_FILENO);
176  	        ::close(ipipe[IN]);
177  	      }
178  	    }
179  	    if (opipe[OUT] >= 0) {
180  	      if (opipe[OUT] == STDOUT_FILENO) {
181  	        ::fcntl(STDOUT_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */
182  	      } else {
183  	        ::dup2(opipe[OUT], STDOUT_FILENO);
184  	        ::close(opipe[OUT]);
185  	        static fd_buf buf(STDOUT_FILENO);
186  	        std::cout.rdbuf(&buf);
187  	      }
188  	    }
189  	    if (epipe[OUT] >= 0) {
190  	      if (epipe[OUT] == STDERR_FILENO) {
191  	        ::fcntl(STDERR_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */
192  	      } else {
193  	        ::dup2(epipe[OUT], STDERR_FILENO);
194  	        ::close(epipe[OUT]);
195  	        static fd_buf buf(STDERR_FILENO);
196  	        std::cerr.rdbuf(&buf);
197  	      }
198  	    }
199  	
200  	    int maxfd = sysconf(_SC_OPEN_MAX);
201  	    if (maxfd == -1)
202  	      maxfd = 16384;
203  	    for (int fd = 0; fd <= maxfd; fd++) {
204  	      if (fd == STDIN_FILENO && stdin_op != CLOSE)
205  		continue;
206  	      if (fd == STDOUT_FILENO && stdout_op != CLOSE)
207  		continue;
208  	      if (fd == STDERR_FILENO && stderr_op != CLOSE)
209  		continue;
210  	      ::close(fd);
211  	    }
212  	
213  	    exec();
214  	    ceph_abort(); // Never reached
215  	  }
216  	
217  	  ret = -errno;
218  	  errstr << "fork failed: " << cpp_strerror(errno);
219  	
220  	fail:
221  	  close(ipipe[0]);
222  	  close(ipipe[1]);
223  	  close(opipe[0]);
224  	  close(opipe[1]);
225  	  close(epipe[0]);
226  	  close(epipe[1]);
227  	
228  	  return ret;
229  	}
230  	
231  	void SubProcess::exec() {
232  	  ceph_assert(is_child());
233  	
234  	  std::vector<const char *> args;
235  	  args.push_back(cmd.c_str());
236  	  for (std::vector<std::string>::iterator i = cmd_args.begin();
237  	       i != cmd_args.end();
238  	       i++) {
239  	    args.push_back(i->c_str());
240  	  }
241  	  args.push_back(NULL);
242  	
243  	  int ret = execvp(cmd.c_str(), (char * const *)&args[0]);
244  	  ceph_assert(ret == -1);
245  	
246  	  std::cerr << cmd << ": exec failed: " << cpp_strerror(errno) << "\n";
247  	  _exit(EXIT_FAILURE);
248  	}
249  	
250  	int SubProcess::join() {
251  	  ceph_assert(is_spawned());
252  	
253  	  close(stdin_pipe_out_fd);
254  	  close(stdout_pipe_in_fd);
255  	  close(stderr_pipe_in_fd);
256  	
257  	  int status;
258  	
259  	  while (waitpid(pid, &status, 0) == -1)
260  	    ceph_assert(errno == EINTR);
261  	
262  	  pid = -1;
263  	
264  	  if (WIFEXITED(status)) {
265  	    if (WEXITSTATUS(status) != EXIT_SUCCESS)
266  	      errstr << cmd << ": exit status: " << WEXITSTATUS(status);
267  	    return WEXITSTATUS(status);
268  	  }
269  	  if (WIFSIGNALED(status)) {
270  	    errstr << cmd << ": got signal: " << WTERMSIG(status);
271  	    return 128 + WTERMSIG(status);
272  	  }
273  	  errstr << cmd << ": waitpid: unknown status returned\n";
274  	  return EXIT_FAILURE;
275  	}
276  	
277  	SubProcessTimed::SubProcessTimed(const char *cmd, std_fd_op stdin_op,
278  					 std_fd_op stdout_op, std_fd_op stderr_op,
279  					 int timeout_, int sigkill_) :
280  	  SubProcess(cmd, stdin_op, stdout_op, stderr_op),
281  	  timeout(timeout_),
282  	  sigkill(sigkill_) {
283  	}
284  	
285  	static bool timedout = false; // only used after fork
286  	void timeout_sighandler(int sig) {
287  	  timedout = true;
288  	}
289  	static void dummy_sighandler(int sig) {}
290  	
291  	void SubProcessTimed::exec() {
292  	  ceph_assert(is_child());
293  	
294  	  if (timeout <= 0) {
295  	    SubProcess::exec();
296  	    ceph_abort(); // Never reached
297  	  }
298  	
299  	  sigset_t mask, oldmask;
300  	  int pid;
301  	
302  	  // Restore default action for SIGTERM in case the parent process decided
303  	  // to ignore it.
304  	  if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
305  	    std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
306  	    goto fail_exit;
307  	  }
308  	  // Because SIGCHLD is ignored by default, setup dummy handler for it,
309  	  // so we can mask it.
310  	  if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) {
311  	    std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
312  	    goto fail_exit;
313  	  }
314  	  // Setup timeout handler.
315  	  if (signal(SIGALRM, timeout_sighandler) == SIG_ERR) {
316  	    std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
317  	    goto fail_exit;
318  	  }
319  	  // Block interesting signals.
320  	  sigemptyset(&mask);
321  	  sigaddset(&mask, SIGINT);
322  	  sigaddset(&mask, SIGTERM);
323  	  sigaddset(&mask, SIGCHLD);
324  	  sigaddset(&mask, SIGALRM);
325  	  if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
326  	    std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n";
327  	    goto fail_exit;
328  	  }
329  	
330  	  pid = fork();
331  	
332  	  if (pid == -1) {
333  	    std::cerr << cmd << ": fork failed: " << cpp_strerror(errno) << "\n";
334  	    goto fail_exit;
335  	  }
336  	
337  	  if (pid == 0) { // Child
338  	    // Restore old sigmask.
339  	    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
340  	      std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n";
341  	      goto fail_exit;
342  	    }
343  	    (void)setpgid(0, 0); // Become process group leader.
344  	    SubProcess::exec();
345  	    ceph_abort(); // Never reached
346  	  }
347  	
348  	  // Parent
349  	  (void)alarm(timeout);
350  	
351  	  for (;;) {
352  	    int signo;
353  	    if (sigwait(&mask, &signo) == -1) {
354  	      std::cerr << cmd << ": sigwait failed: " << cpp_strerror(errno) << "\n";
355  	      goto fail_exit;
356  	    }
357  	    switch (signo) {
358  	    case SIGCHLD:
359  	      int status;
360  	      if (waitpid(pid, &status, WNOHANG) == -1) {
361  		std::cerr << cmd << ": waitpid failed: " << cpp_strerror(errno) << "\n";
362  		goto fail_exit;
363  	      }
364  	      if (WIFEXITED(status))
365  		_exit(WEXITSTATUS(status));
366  	      if (WIFSIGNALED(status))
367  		_exit(128 + WTERMSIG(status));
368  	      std::cerr << cmd << ": unknown status returned\n";
369  	      goto fail_exit;
370  	    case SIGINT:
371  	    case SIGTERM:
372  	      // Pass SIGINT and SIGTERM, which are usually used to terminate
373  	      // a process, to the child.
374  	      if (::kill(pid, signo) == -1) {
375  		std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n";
376  		goto fail_exit;
377  	      }
378  	      continue;
379  	    case SIGALRM:
380  	      std::cerr << cmd << ": timed out (" << timeout << " sec)\n";
381  	      if (::killpg(pid, sigkill) == -1) {
382  		std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n";
383  		goto fail_exit;
384  	      }
385  	      continue;
386  	    default:
387  	      std::cerr << cmd << ": sigwait: invalid signal: " << signo << "\n";
388  	      goto fail_exit;
389  	    }
390  	  }
391  	
392  	fail_exit:
393  	  _exit(EXIT_FAILURE);
394  	}
395