]>
git.proxmox.com Git - ceph.git/blob - ceph/src/common/SubProcess.cc
1 #include "SubProcess.h"
3 #if defined(__FreeBSD__) || defined(__APPLE__)
12 #include "common/errno.h"
13 #include "include/ceph_assert.h"
14 #include "include/compat.h"
16 SubProcess::SubProcess(const char *cmd_
, std_fd_op stdin_op_
, std_fd_op stdout_op_
, std_fd_op stderr_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),
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);
36 void SubProcess::add_cmd_args(const char *arg
, ...) {
37 ceph_assert(!is_spawned());
44 p
= va_arg(ap
, const char*);
49 void SubProcess::add_cmd_arg(const char *arg
) {
50 ceph_assert(!is_spawned());
52 cmd_args
.push_back(arg
);
55 int SubProcess::get_stdin() const {
56 ceph_assert(is_spawned());
57 ceph_assert(stdin_op
== PIPE
);
59 return stdin_pipe_out_fd
;
62 int SubProcess::get_stdout() const {
63 ceph_assert(is_spawned());
64 ceph_assert(stdout_op
== PIPE
);
66 return stdout_pipe_in_fd
;
69 int SubProcess::get_stderr() const {
70 ceph_assert(is_spawned());
71 ceph_assert(stderr_op
== PIPE
);
73 return stderr_pipe_in_fd
;
76 void SubProcess::close(int &fd
) {
84 void SubProcess::close_stdin() {
85 ceph_assert(is_spawned());
86 ceph_assert(stdin_op
== PIPE
);
88 close(stdin_pipe_out_fd
);
91 void SubProcess::close_stdout() {
92 ceph_assert(is_spawned());
93 ceph_assert(stdout_op
== PIPE
);
95 close(stdout_pipe_in_fd
);
98 void SubProcess::close_stderr() {
99 ceph_assert(is_spawned());
100 ceph_assert(stderr_op
== PIPE
);
102 close(stderr_pipe_in_fd
);
105 void SubProcess::kill(int signo
) const {
106 ceph_assert(is_spawned());
108 int ret
= ::kill(pid
, signo
);
109 ceph_assert(ret
== 0);
112 const std::string
SubProcess::err() const {
116 class fd_buf
: public std::streambuf
{
119 fd_buf (int fd
) : fd(fd
)
122 int_type
overflow (int_type c
) override
{
123 if (c
== EOF
) return EOF
;
125 if (write (fd
, &buf
, 1) != 1) {
130 std::streamsize
xsputn (const char* s
, std::streamsize count
) override
{
131 return write(fd
, s
, count
);
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);
141 enum { IN
= 0, OUT
= 1 };
143 int ipipe
[2], opipe
[2], epipe
[2];
145 ipipe
[0] = ipipe
[1] = opipe
[0] = opipe
[1] = epipe
[0] = epipe
[1] = -1;
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)) {
153 errstr
<< "pipe failed: " << cpp_strerror(errno
);
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
]);
166 if (pid
== 0) { // Child
171 if (ipipe
[IN
] >= 0) {
172 if (ipipe
[IN
] == STDIN_FILENO
) {
173 ::fcntl(STDIN_FILENO
, F_SETFD
, 0); /* clear FD_CLOEXEC */
175 ::dup2(ipipe
[IN
], STDIN_FILENO
);
179 if (opipe
[OUT
] >= 0) {
180 if (opipe
[OUT
] == STDOUT_FILENO
) {
181 ::fcntl(STDOUT_FILENO
, F_SETFD
, 0); /* clear FD_CLOEXEC */
183 ::dup2(opipe
[OUT
], STDOUT_FILENO
);
185 static fd_buf
buf(STDOUT_FILENO
);
186 std::cout
.rdbuf(&buf
);
189 if (epipe
[OUT
] >= 0) {
190 if (epipe
[OUT
] == STDERR_FILENO
) {
191 ::fcntl(STDERR_FILENO
, F_SETFD
, 0); /* clear FD_CLOEXEC */
193 ::dup2(epipe
[OUT
], STDERR_FILENO
);
195 static fd_buf
buf(STDERR_FILENO
);
196 std::cerr
.rdbuf(&buf
);
200 int maxfd
= sysconf(_SC_OPEN_MAX
);
203 for (int fd
= 0; fd
<= maxfd
; fd
++) {
204 if (fd
== STDIN_FILENO
&& stdin_op
!= CLOSE
)
206 if (fd
== STDOUT_FILENO
&& stdout_op
!= CLOSE
)
208 if (fd
== STDERR_FILENO
&& stderr_op
!= CLOSE
)
214 ceph_abort(); // Never reached
218 errstr
<< "fork failed: " << cpp_strerror(errno
);
231 void SubProcess::exec() {
232 ceph_assert(is_child());
234 std::vector
<const char *> args
;
235 args
.push_back(cmd
.c_str());
236 for (std::vector
<std::string
>::iterator i
= cmd_args
.begin();
239 args
.push_back(i
->c_str());
241 args
.push_back(NULL
);
243 int ret
= execvp(cmd
.c_str(), (char * const *)&args
[0]);
244 ceph_assert(ret
== -1);
246 std::cerr
<< cmd
<< ": exec failed: " << cpp_strerror(errno
) << "\n";
250 int SubProcess::join() {
251 ceph_assert(is_spawned());
253 close(stdin_pipe_out_fd
);
254 close(stdout_pipe_in_fd
);
255 close(stderr_pipe_in_fd
);
259 while (waitpid(pid
, &status
, 0) == -1)
260 ceph_assert(errno
== EINTR
);
264 if (WIFEXITED(status
)) {
265 if (WEXITSTATUS(status
) != EXIT_SUCCESS
)
266 errstr
<< cmd
<< ": exit status: " << WEXITSTATUS(status
);
267 return WEXITSTATUS(status
);
269 if (WIFSIGNALED(status
)) {
270 errstr
<< cmd
<< ": got signal: " << WTERMSIG(status
);
271 return 128 + WTERMSIG(status
);
273 errstr
<< cmd
<< ": waitpid: unknown status returned\n";
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
),
285 static bool timedout
= false; // only used after fork
286 void timeout_sighandler(int sig
) {
289 static void dummy_sighandler(int sig
) {}
291 void SubProcessTimed::exec() {
292 ceph_assert(is_child());
296 ceph_abort(); // Never reached
299 sigset_t mask
, oldmask
;
302 // Restore default action for SIGTERM in case the parent process decided
304 if (signal(SIGTERM
, SIG_DFL
) == SIG_ERR
) {
305 std::cerr
<< cmd
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
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";
314 // Setup timeout handler.
315 if (signal(SIGALRM
, timeout_sighandler
) == SIG_ERR
) {
316 std::cerr
<< cmd
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
319 // Block interesting signals.
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";
333 std::cerr
<< cmd
<< ": fork failed: " << cpp_strerror(errno
) << "\n";
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";
343 (void)setpgid(0, 0); // Become process group leader.
345 ceph_abort(); // Never reached
349 (void)alarm(timeout
);
353 if (sigwait(&mask
, &signo
) == -1) {
354 std::cerr
<< cmd
<< ": sigwait failed: " << cpp_strerror(errno
) << "\n";
360 if (waitpid(pid
, &status
, WNOHANG
) == -1) {
361 std::cerr
<< cmd
<< ": waitpid failed: " << cpp_strerror(errno
) << "\n";
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";
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";
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";
387 std::cerr
<< cmd
<< ": sigwait: invalid signal: " << signo
<< "\n";