]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/SubProcess.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / common / SubProcess.cc
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
29 SubProcess::~SubProcess() {
30 ceph_assert(!is_spawned());
31 ceph_assert(stdin_pipe_out_fd == -1);
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) == -1) ||
150 (stdout_op == PIPE && pipe_cloexec(opipe) == -1) ||
151 (stderr_op == PIPE && pipe_cloexec(epipe) == -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 }