]>
git.proxmox.com Git - ceph.git/blob - ceph/src/common/SubProcess.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph distributed storage system
6 * Copyright (C) 2015 Mirantis Inc
8 * Author: Mykola Golub <mgolub@mirantis.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
25 #include <include/assert.h>
26 #include <common/errno.h>
27 #if defined(__FreeBSD__)
28 #include <sys/types.h>
34 * A helper class to spawn a subprocess.
38 * SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE);
39 * if (cat.spawn() != 0) {
40 * std::cerr << "cat failed: " << cat.err() << std::endl;
43 * write_to_fd(cat.get_stdout(), "hello world!\n");
45 * read_from_fd(cat.get_stdin(), buf);
46 * if (cat.join() != 0) {
47 * std::cerr << cat.err() << std::endl;
60 SubProcess(const char *cmd
,
61 std_fd_op stdin_op
= CLOSE
,
62 std_fd_op stdout_op
= CLOSE
,
63 std_fd_op stderr_op
= CLOSE
);
64 virtual ~SubProcess();
66 void add_cmd_args(const char *arg
, ...);
67 void add_cmd_arg(const char *arg
);
69 virtual int spawn(); // Returns 0 on success or -errno on failure.
70 virtual int join(); // Returns exit code (0 on success).
72 bool is_spawned() const { return pid
> 0; }
74 int get_stdin() const;
75 int get_stdout() const;
76 int get_stderr() const;
82 void kill(int signo
= SIGTERM
) const;
84 const std::string
err() const;
87 bool is_child() const { return pid
== 0; }
95 std::vector
<std::string
> cmd_args
;
99 int stdin_pipe_out_fd
;
100 int stdout_pipe_in_fd
;
101 int stderr_pipe_in_fd
;
103 std::ostringstream errstr
;
106 class SubProcessTimed
: public SubProcess
{
108 SubProcessTimed(const char *cmd
, std_fd_op stdin_op
= CLOSE
,
109 std_fd_op stdout_op
= CLOSE
, std_fd_op stderr_op
= CLOSE
,
110 int timeout
= 0, int sigkill
= SIGKILL
);
113 void exec() override
;
120 inline SubProcess::SubProcess(const char *cmd_
, std_fd_op stdin_op_
, std_fd_op stdout_op_
, std_fd_op stderr_op_
) :
124 stdout_op(stdout_op_
),
125 stderr_op(stderr_op_
),
126 stdin_pipe_out_fd(-1),
127 stdout_pipe_in_fd(-1),
128 stderr_pipe_in_fd(-1),
133 inline SubProcess::~SubProcess() {
134 assert(!is_spawned());
135 assert(stdin_pipe_out_fd
== -1);
136 assert(stdout_pipe_in_fd
== -1);
137 assert(stderr_pipe_in_fd
== -1);
140 inline void SubProcess::add_cmd_args(const char *arg
, ...) {
141 assert(!is_spawned());
148 p
= va_arg(ap
, const char*);
153 inline void SubProcess::add_cmd_arg(const char *arg
) {
154 assert(!is_spawned());
156 cmd_args
.push_back(arg
);
159 inline int SubProcess::get_stdin() const {
160 assert(is_spawned());
161 assert(stdin_op
== PIPE
);
163 return stdin_pipe_out_fd
;
166 inline int SubProcess::get_stdout() const {
167 assert(is_spawned());
168 assert(stdout_op
== PIPE
);
170 return stdout_pipe_in_fd
;
173 inline int SubProcess::get_stderr() const {
174 assert(is_spawned());
175 assert(stderr_op
== PIPE
);
177 return stderr_pipe_in_fd
;
180 inline void SubProcess::close(int &fd
) {
188 inline void SubProcess::close_stdin() {
189 assert(is_spawned());
190 assert(stdin_op
== PIPE
);
192 close(stdin_pipe_out_fd
);
195 inline void SubProcess::close_stdout() {
196 assert(is_spawned());
197 assert(stdout_op
== PIPE
);
199 close(stdout_pipe_in_fd
);
202 inline void SubProcess::close_stderr() {
203 assert(is_spawned());
204 assert(stderr_op
== PIPE
);
206 close(stderr_pipe_in_fd
);
209 inline void SubProcess::kill(int signo
) const {
210 assert(is_spawned());
212 int ret
= ::kill(pid
, signo
);
216 inline const std::string
SubProcess::err() const {
220 class fd_buf
: public std::streambuf
{
223 fd_buf (int fd
) : fd(fd
)
226 int_type
overflow (int_type c
) override
{
227 if (c
== EOF
) return EOF
;
229 if (write (fd
, &buf
, 1) != 1) {
234 std::streamsize
xsputn (const char* s
, std::streamsize count
) override
{
235 return write(fd
, s
, count
);
239 inline int SubProcess::spawn() {
240 assert(!is_spawned());
241 assert(stdin_pipe_out_fd
== -1);
242 assert(stdout_pipe_in_fd
== -1);
243 assert(stderr_pipe_in_fd
== -1);
245 enum { IN
= 0, OUT
= 1 };
247 int ipipe
[2], opipe
[2], epipe
[2];
249 ipipe
[0] = ipipe
[1] = opipe
[0] = opipe
[1] = epipe
[0] = epipe
[1] = -1;
253 if ((stdin_op
== PIPE
&& ::pipe(ipipe
) == -1) ||
254 (stdout_op
== PIPE
&& ::pipe(opipe
) == -1) ||
255 (stderr_op
== PIPE
&& ::pipe(epipe
) == -1)) {
257 errstr
<< "pipe failed: " << cpp_strerror(errno
);
263 if (pid
> 0) { // Parent
264 stdin_pipe_out_fd
= ipipe
[OUT
]; close(ipipe
[IN
]);
265 stdout_pipe_in_fd
= opipe
[IN
]; close(opipe
[OUT
]);
266 stderr_pipe_in_fd
= epipe
[IN
]; close(epipe
[OUT
]);
270 if (pid
== 0) { // Child
275 if (ipipe
[IN
] != -1 && ipipe
[IN
] != STDIN_FILENO
) {
276 ::dup2(ipipe
[IN
], STDIN_FILENO
);
279 if (opipe
[OUT
] != -1 && opipe
[OUT
] != STDOUT_FILENO
) {
280 ::dup2(opipe
[OUT
], STDOUT_FILENO
);
282 static fd_buf
buf(STDOUT_FILENO
);
283 std::cout
.rdbuf(&buf
);
285 if (epipe
[OUT
] != -1 && epipe
[OUT
] != STDERR_FILENO
) {
286 ::dup2(epipe
[OUT
], STDERR_FILENO
);
288 static fd_buf
buf(STDERR_FILENO
);
289 std::cerr
.rdbuf(&buf
);
292 int maxfd
= sysconf(_SC_OPEN_MAX
);
295 for (int fd
= 0; fd
<= maxfd
; fd
++) {
296 if (fd
== STDIN_FILENO
&& stdin_op
!= CLOSE
)
298 if (fd
== STDOUT_FILENO
&& stdout_op
!= CLOSE
)
300 if (fd
== STDERR_FILENO
&& stderr_op
!= CLOSE
)
306 ceph_abort(); // Never reached
310 errstr
<< "fork failed: " << cpp_strerror(errno
);
323 inline void SubProcess::exec() {
326 std::vector
<const char *> args
;
327 args
.push_back(cmd
.c_str());
328 for (std::vector
<std::string
>::iterator i
= cmd_args
.begin();
331 args
.push_back(i
->c_str());
333 args
.push_back(NULL
);
335 int ret
= execvp(cmd
.c_str(), (char * const *)&args
[0]);
338 std::cerr
<< cmd
<< ": exec failed: " << cpp_strerror(errno
) << "\n";
342 inline int SubProcess::join() {
343 assert(is_spawned());
345 close(stdin_pipe_out_fd
);
346 close(stdout_pipe_in_fd
);
347 close(stderr_pipe_in_fd
);
351 while (waitpid(pid
, &status
, 0) == -1)
352 assert(errno
== EINTR
);
356 if (WIFEXITED(status
)) {
357 if (WEXITSTATUS(status
) != EXIT_SUCCESS
)
358 errstr
<< cmd
<< ": exit status: " << WEXITSTATUS(status
);
359 return WEXITSTATUS(status
);
361 if (WIFSIGNALED(status
)) {
362 errstr
<< cmd
<< ": got signal: " << WTERMSIG(status
);
363 return 128 + WTERMSIG(status
);
365 errstr
<< cmd
<< ": waitpid: unknown status returned\n";
369 inline SubProcessTimed::SubProcessTimed(const char *cmd
, std_fd_op stdin_op
,
370 std_fd_op stdout_op
, std_fd_op stderr_op
,
371 int timeout_
, int sigkill_
) :
372 SubProcess(cmd
, stdin_op
, stdout_op
, stderr_op
),
377 static bool timedout
= false; // only used after fork
378 static void timeout_sighandler(int sig
) {
381 static void dummy_sighandler(int sig
) {}
383 inline void SubProcessTimed::exec() {
388 ceph_abort(); // Never reached
391 sigset_t mask
, oldmask
;
394 // Restore default action for SIGTERM in case the parent process decided
396 if (signal(SIGTERM
, SIG_DFL
) == SIG_ERR
) {
397 std::cerr
<< cmd
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
400 // Because SIGCHLD is ignored by default, setup dummy handler for it,
401 // so we can mask it.
402 if (signal(SIGCHLD
, dummy_sighandler
) == SIG_ERR
) {
403 std::cerr
<< cmd
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
406 // Setup timeout handler.
407 if (signal(SIGALRM
, timeout_sighandler
) == SIG_ERR
) {
408 std::cerr
<< cmd
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
411 // Block interesting signals.
413 sigaddset(&mask
, SIGINT
);
414 sigaddset(&mask
, SIGTERM
);
415 sigaddset(&mask
, SIGCHLD
);
416 sigaddset(&mask
, SIGALRM
);
417 if (sigprocmask(SIG_SETMASK
, &mask
, &oldmask
) == -1) {
418 std::cerr
<< cmd
<< ": sigprocmask failed: " << cpp_strerror(errno
) << "\n";
425 std::cerr
<< cmd
<< ": fork failed: " << cpp_strerror(errno
) << "\n";
429 if (pid
== 0) { // Child
430 // Restore old sigmask.
431 if (sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == -1) {
432 std::cerr
<< cmd
<< ": sigprocmask failed: " << cpp_strerror(errno
) << "\n";
435 (void)setpgid(0, 0); // Become process group leader.
437 ceph_abort(); // Never reached
441 (void)alarm(timeout
);
445 if (sigwait(&mask
, &signo
) == -1) {
446 std::cerr
<< cmd
<< ": sigwait failed: " << cpp_strerror(errno
) << "\n";
452 if (waitpid(pid
, &status
, WNOHANG
) == -1) {
453 std::cerr
<< cmd
<< ": waitpid failed: " << cpp_strerror(errno
) << "\n";
456 if (WIFEXITED(status
))
457 _exit(WEXITSTATUS(status
));
458 if (WIFSIGNALED(status
))
459 _exit(128 + WTERMSIG(status
));
460 std::cerr
<< cmd
<< ": unknown status returned\n";
464 // Pass SIGINT and SIGTERM, which are usually used to terminate
465 // a process, to the child.
466 if (::kill(pid
, signo
) == -1) {
467 std::cerr
<< cmd
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
472 std::cerr
<< cmd
<< ": timed out (" << timeout
<< " sec)\n";
473 if (::killpg(pid
, sigkill
) == -1) {
474 std::cerr
<< cmd
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
479 std::cerr
<< cmd
<< ": sigwait: invalid signal: " << signo
<< "\n";