X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fcommon%2FSubProcess.h;fp=src%2Fceph%2Fsrc%2Fcommon%2FSubProcess.h;h=0000000000000000000000000000000000000000;hb=7da45d65be36d36b880cc55c5036e96c24b53f00;hp=ce9559c5eeca10dfec77eb15888237431d19a358;hpb=691462d09d0987b47e112d6ee8740375df3c51b2;p=stor4nfv.git diff --git a/src/ceph/src/common/SubProcess.h b/src/ceph/src/common/SubProcess.h deleted file mode 100644 index ce9559c..0000000 --- a/src/ceph/src/common/SubProcess.h +++ /dev/null @@ -1,488 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab -/* - * Ceph distributed storage system - * - * Copyright (C) 2015 Mirantis Inc - * - * Author: Mykola Golub - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - */ - -#ifndef SUB_PROCESS_H -#define SUB_PROCESS_H - -#include -#include -#include -#include -#include -#include -#include -#if defined(__FreeBSD__) -#include -#include -#endif - -/** - * SubProcess: - * A helper class to spawn a subprocess. - * - * Example: - * - * SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE); - * if (cat.spawn() != 0) { - * std::cerr << "cat failed: " << cat.err() << std::endl; - * return false; - * } - * write_to_fd(cat.get_stdout(), "hello world!\n"); - * cat.close_stdout(); - * read_from_fd(cat.get_stdin(), buf); - * if (cat.join() != 0) { - * std::cerr << cat.err() << std::endl; - * return false; - * } - */ - -class SubProcess { -public: - enum std_fd_op{ - KEEP, - CLOSE, - PIPE - }; -public: - SubProcess(const char *cmd, - std_fd_op stdin_op = CLOSE, - std_fd_op stdout_op = CLOSE, - std_fd_op stderr_op = CLOSE); - virtual ~SubProcess(); - - void add_cmd_args(const char *arg, ...); - void add_cmd_arg(const char *arg); - - virtual int spawn(); // Returns 0 on success or -errno on failure. - virtual int join(); // Returns exit code (0 on success). - - bool is_spawned() const { return pid > 0; } - - int get_stdin() const; - int get_stdout() const; - int get_stderr() const; - - void close_stdin(); - void close_stdout(); - void close_stderr(); - - void kill(int signo = SIGTERM) const; - - const std::string err() const; - -protected: - bool is_child() const { return pid == 0; } - virtual void exec(); - -private: - void close(int &fd); - -protected: - std::string cmd; - std::vector cmd_args; - std_fd_op stdin_op; - std_fd_op stdout_op; - std_fd_op stderr_op; - int stdin_pipe_out_fd; - int stdout_pipe_in_fd; - int stderr_pipe_in_fd; - int pid; - std::ostringstream errstr; -}; - -class SubProcessTimed : public SubProcess { -public: - SubProcessTimed(const char *cmd, std_fd_op stdin_op = CLOSE, - std_fd_op stdout_op = CLOSE, std_fd_op stderr_op = CLOSE, - int timeout = 0, int sigkill = SIGKILL); - -protected: - void exec() override; - -private: - int timeout; - int sigkill; -}; - -inline SubProcess::SubProcess(const char *cmd_, std_fd_op stdin_op_, std_fd_op stdout_op_, std_fd_op stderr_op_) : - cmd(cmd_), - cmd_args(), - stdin_op(stdin_op_), - stdout_op(stdout_op_), - stderr_op(stderr_op_), - stdin_pipe_out_fd(-1), - stdout_pipe_in_fd(-1), - stderr_pipe_in_fd(-1), - pid(-1), - errstr() { -} - -inline SubProcess::~SubProcess() { - assert(!is_spawned()); - assert(stdin_pipe_out_fd == -1); - assert(stdout_pipe_in_fd == -1); - assert(stderr_pipe_in_fd == -1); -} - -inline void SubProcess::add_cmd_args(const char *arg, ...) { - assert(!is_spawned()); - - va_list ap; - va_start(ap, arg); - const char *p = arg; - do { - add_cmd_arg(p); - p = va_arg(ap, const char*); - } while (p != NULL); - va_end(ap); -} - -inline void SubProcess::add_cmd_arg(const char *arg) { - assert(!is_spawned()); - - cmd_args.push_back(arg); -} - -inline int SubProcess::get_stdin() const { - assert(is_spawned()); - assert(stdin_op == PIPE); - - return stdin_pipe_out_fd; -} - -inline int SubProcess::get_stdout() const { - assert(is_spawned()); - assert(stdout_op == PIPE); - - return stdout_pipe_in_fd; -} - -inline int SubProcess::get_stderr() const { - assert(is_spawned()); - assert(stderr_op == PIPE); - - return stderr_pipe_in_fd; -} - -inline void SubProcess::close(int &fd) { - if (fd == -1) - return; - - ::close(fd); - fd = -1; -} - -inline void SubProcess::close_stdin() { - assert(is_spawned()); - assert(stdin_op == PIPE); - - close(stdin_pipe_out_fd); -} - -inline void SubProcess::close_stdout() { - assert(is_spawned()); - assert(stdout_op == PIPE); - - close(stdout_pipe_in_fd); -} - -inline void SubProcess::close_stderr() { - assert(is_spawned()); - assert(stderr_op == PIPE); - - close(stderr_pipe_in_fd); -} - -inline void SubProcess::kill(int signo) const { - assert(is_spawned()); - - int ret = ::kill(pid, signo); - assert(ret == 0); -} - -inline const std::string SubProcess::err() const { - return errstr.str(); -} - -class fd_buf : public std::streambuf { - int fd; -public: - fd_buf (int fd) : fd(fd) - {} -protected: - int_type overflow (int_type c) override { - if (c == EOF) return EOF; - char buf = c; - if (write (fd, &buf, 1) != 1) { - return EOF; - } - return c; - } - std::streamsize xsputn (const char* s, std::streamsize count) override { - return write(fd, s, count); - } -}; - -inline int SubProcess::spawn() { - assert(!is_spawned()); - assert(stdin_pipe_out_fd == -1); - assert(stdout_pipe_in_fd == -1); - assert(stderr_pipe_in_fd == -1); - - enum { IN = 0, OUT = 1 }; - - int ipipe[2], opipe[2], epipe[2]; - - ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = epipe[0] = epipe[1] = -1; - - int ret = 0; - - if ((stdin_op == PIPE && ::pipe(ipipe) == -1) || - (stdout_op == PIPE && ::pipe(opipe) == -1) || - (stderr_op == PIPE && ::pipe(epipe) == -1)) { - ret = -errno; - errstr << "pipe failed: " << cpp_strerror(errno); - goto fail; - } - - pid = fork(); - - if (pid > 0) { // Parent - stdin_pipe_out_fd = ipipe[OUT]; close(ipipe[IN ]); - stdout_pipe_in_fd = opipe[IN ]; close(opipe[OUT]); - stderr_pipe_in_fd = epipe[IN ]; close(epipe[OUT]); - return 0; - } - - if (pid == 0) { // Child - close(ipipe[OUT]); - close(opipe[IN ]); - close(epipe[IN ]); - - if (ipipe[IN] != -1 && ipipe[IN] != STDIN_FILENO) { - ::dup2(ipipe[IN], STDIN_FILENO); - close(ipipe[IN]); - } - if (opipe[OUT] != -1 && opipe[OUT] != STDOUT_FILENO) { - ::dup2(opipe[OUT], STDOUT_FILENO); - close(opipe[OUT]); - static fd_buf buf(STDOUT_FILENO); - std::cout.rdbuf(&buf); - } - if (epipe[OUT] != -1 && epipe[OUT] != STDERR_FILENO) { - ::dup2(epipe[OUT], STDERR_FILENO); - close(epipe[OUT]); - static fd_buf buf(STDERR_FILENO); - std::cerr.rdbuf(&buf); - } - - int maxfd = sysconf(_SC_OPEN_MAX); - if (maxfd == -1) - maxfd = 16384; - for (int fd = 0; fd <= maxfd; fd++) { - if (fd == STDIN_FILENO && stdin_op != CLOSE) - continue; - if (fd == STDOUT_FILENO && stdout_op != CLOSE) - continue; - if (fd == STDERR_FILENO && stderr_op != CLOSE) - continue; - ::close(fd); - } - - exec(); - ceph_abort(); // Never reached - } - - ret = -errno; - errstr << "fork failed: " << cpp_strerror(errno); - -fail: - close(ipipe[0]); - close(ipipe[1]); - close(opipe[0]); - close(opipe[1]); - close(epipe[0]); - close(epipe[1]); - - return ret; -} - -inline void SubProcess::exec() { - assert(is_child()); - - std::vector args; - args.push_back(cmd.c_str()); - for (std::vector::iterator i = cmd_args.begin(); - i != cmd_args.end(); - i++) { - args.push_back(i->c_str()); - } - args.push_back(NULL); - - int ret = execvp(cmd.c_str(), (char * const *)&args[0]); - assert(ret == -1); - - std::cerr << cmd << ": exec failed: " << cpp_strerror(errno) << "\n"; - _exit(EXIT_FAILURE); -} - -inline int SubProcess::join() { - assert(is_spawned()); - - close(stdin_pipe_out_fd); - close(stdout_pipe_in_fd); - close(stderr_pipe_in_fd); - - int status; - - while (waitpid(pid, &status, 0) == -1) - assert(errno == EINTR); - - pid = -1; - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != EXIT_SUCCESS) - errstr << cmd << ": exit status: " << WEXITSTATUS(status); - return WEXITSTATUS(status); - } - if (WIFSIGNALED(status)) { - errstr << cmd << ": got signal: " << WTERMSIG(status); - return 128 + WTERMSIG(status); - } - errstr << cmd << ": waitpid: unknown status returned\n"; - return EXIT_FAILURE; -} - -inline SubProcessTimed::SubProcessTimed(const char *cmd, std_fd_op stdin_op, - std_fd_op stdout_op, std_fd_op stderr_op, - int timeout_, int sigkill_) : - SubProcess(cmd, stdin_op, stdout_op, stderr_op), - timeout(timeout_), - sigkill(sigkill_) { -} - -static bool timedout = false; // only used after fork -static void timeout_sighandler(int sig) { - timedout = true; -} -static void dummy_sighandler(int sig) {} - -inline void SubProcessTimed::exec() { - assert(is_child()); - - if (timeout <= 0) { - SubProcess::exec(); - ceph_abort(); // Never reached - } - - sigset_t mask, oldmask; - int pid; - - // Restore default action for SIGTERM in case the parent process decided - // to ignore it. - if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { - std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - // Because SIGCHLD is ignored by default, setup dummy handler for it, - // so we can mask it. - if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) { - std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - // Setup timeout handler. - if (signal(SIGALRM, timeout_sighandler) == SIG_ERR) { - std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - // Block interesting signals. - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGALRM); - if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) { - std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - - pid = fork(); - - if (pid == -1) { - std::cerr << cmd << ": fork failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - - if (pid == 0) { // Child - // Restore old sigmask. - if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) { - std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - (void)setpgid(0, 0); // Become process group leader. - SubProcess::exec(); - ceph_abort(); // Never reached - } - - // Parent - (void)alarm(timeout); - - for (;;) { - int signo; - if (sigwait(&mask, &signo) == -1) { - std::cerr << cmd << ": sigwait failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - switch (signo) { - case SIGCHLD: - int status; - if (waitpid(pid, &status, WNOHANG) == -1) { - std::cerr << cmd << ": waitpid failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - if (WIFEXITED(status)) - _exit(WEXITSTATUS(status)); - if (WIFSIGNALED(status)) - _exit(128 + WTERMSIG(status)); - std::cerr << cmd << ": unknown status returned\n"; - goto fail_exit; - case SIGINT: - case SIGTERM: - // Pass SIGINT and SIGTERM, which are usually used to terminate - // a process, to the child. - if (::kill(pid, signo) == -1) { - std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - continue; - case SIGALRM: - std::cerr << cmd << ": timed out (" << timeout << " sec)\n"; - if (::killpg(pid, sigkill) == -1) { - std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n"; - goto fail_exit; - } - continue; - default: - std::cerr << cmd << ": sigwait: invalid signal: " << signo << "\n"; - goto fail_exit; - } - } - -fail_exit: - _exit(EXIT_FAILURE); -} - -#endif