summaryrefslogtreecommitdiffstats
path: root/src/common/SubProcess.h
diff options
context:
space:
mode:
authorMykola Golub <mgolub@mirantis.com>2015-02-21 13:41:55 +0100
committerMykola Golub <mgolub@mirantis.com>2015-03-27 09:08:21 +0100
commit538852133bab387bbb28f32262eaaea17b18ed89 (patch)
treeabe904b0dac21a493e8ff89ca9beb52d5a4cf5bd /src/common/SubProcess.h
parentMerge pull request #4171 from majianpeng/cleanup (diff)
downloadceph-538852133bab387bbb28f32262eaaea17b18ed89.tar.xz
ceph-538852133bab387bbb28f32262eaaea17b18ed89.zip
common: SubProcess: helper class to spawn subprocess
Signed-off-by: Mykola Golub <mgolub@mirantis.com>
Diffstat (limited to 'src/common/SubProcess.h')
-rw-r--r--src/common/SubProcess.h329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/common/SubProcess.h b/src/common/SubProcess.h
new file mode 100644
index 00000000000..abee34a1bbd
--- /dev/null
+++ b/src/common/SubProcess.h
@@ -0,0 +1,329 @@
+// -*- 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 <mgolub@mirantis.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sstream>
+#include <vector>
+
+#include <include/assert.h>
+#include <common/errno.h>
+
+/**
+ * SubProcess:
+ * A helper class to spawn a subprocess.
+ *
+ * Example:
+ *
+ * SubProcess cat("cat", true, true);
+ * if (cat.spawn() != 0) {
+ * std::cerr << "cat failed: " << cat.err() << std::endl;
+ * return false;
+ * }
+ * write_to_fd(cat.stdout(), "hello world!\n");
+ * cat.close_stdout();
+ * read_from_fd(cat.stdin(), buf);
+ * if (cat.join() != 0) {
+ * std::cerr << cat.err() << std::endl;
+ * return false;
+ * }
+ */
+
+class SubProcess {
+public:
+ SubProcess(const char *cmd, bool pipe_stdin = false, bool pipe_stdout = false,
+ bool pipe_stderr = false);
+ 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 stdin() const;
+ int stdout() const;
+ int stderr() const;
+
+ void close_stdin();
+ void close_stdout();
+ void close_stderr();
+
+ void kill(int signo = SIGTERM) const;
+
+ const char* err() const;
+
+protected:
+ bool is_child() const { return pid == 0; }
+ virtual void exec();
+
+private:
+ void close(int &fd);
+
+protected:
+ std::string cmd;
+ std::vector<std::string> cmd_args;
+ bool pipe_stdin;
+ bool pipe_stdout;
+ bool pipe_stderr;
+ int stdin_pipe_out_fd;
+ int stdout_pipe_in_fd;
+ int stderr_pipe_in_fd;
+ int pid;
+ std::ostringstream errstr;
+};
+
+SubProcess::SubProcess(const char *cmd_, bool stdin, bool stdout, bool stderr) :
+ cmd(cmd_),
+ cmd_args(),
+ pipe_stdin(stdin),
+ pipe_stdout(stdout),
+ pipe_stderr(stderr),
+ stdin_pipe_out_fd(-1),
+ stdout_pipe_in_fd(-1),
+ stderr_pipe_in_fd(-1),
+ pid(-1),
+ errstr() {
+}
+
+SubProcess::~SubProcess() {
+ assert(!is_spawned());
+ assert(stdin_pipe_out_fd == -1);
+ assert(stdout_pipe_in_fd == -1);
+ assert(stderr_pipe_in_fd == -1);
+}
+
+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);
+}
+
+void SubProcess::add_cmd_arg(const char *arg) {
+ assert(!is_spawned());
+
+ cmd_args.push_back(arg);
+}
+
+int SubProcess::stdin() const {
+ assert(is_spawned());
+ assert(pipe_stdin);
+
+ return stdin_pipe_out_fd;
+}
+
+int SubProcess::stdout() const {
+ assert(is_spawned());
+ assert(pipe_stdout);
+
+ return stdout_pipe_in_fd;
+}
+
+int SubProcess::stderr() const {
+ assert(is_spawned());
+ assert(pipe_stderr);
+
+ return stderr_pipe_in_fd;
+}
+
+void SubProcess::close(int &fd) {
+ if (fd == -1)
+ return;
+
+ ::close(fd);
+ fd = -1;
+}
+
+void SubProcess::close_stdin() {
+ assert(is_spawned());
+ assert(pipe_stdin);
+
+ close(stdin_pipe_out_fd);
+}
+
+void SubProcess::close_stdout() {
+ assert(is_spawned());
+ assert(pipe_stdout);
+
+ close(stdout_pipe_in_fd);
+}
+
+void SubProcess::close_stderr() {
+ assert(is_spawned());
+ assert(pipe_stderr);
+
+ close(stderr_pipe_in_fd);
+}
+
+void SubProcess::kill(int signo) const {
+ assert(is_spawned());
+
+ int ret = ::kill(pid, signo);
+ assert(ret == 0);
+}
+
+const char* SubProcess::err() const {
+ return errstr.str().c_str();
+}
+
+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 ((pipe_stdin && ::pipe(ipipe) == -1) ||
+ (pipe_stdout && ::pipe(opipe) == -1) ||
+ (pipe_stderr && ::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]);
+ }
+ if (epipe[OUT] != -1 && epipe[OUT] != STDERR_FILENO) {
+ ::dup2(epipe[OUT], STDERR_FILENO);
+ close(epipe[OUT]);
+ }
+
+ int maxfd = sysconf(_SC_OPEN_MAX);
+ if (maxfd == -1)
+ maxfd = 16384;
+ for (int fd = 0; fd <= maxfd; fd++) {
+ if (fd == STDIN_FILENO && pipe_stdin)
+ continue;
+ if (fd == STDOUT_FILENO && pipe_stdout)
+ continue;
+ if (fd == STDERR_FILENO && pipe_stderr)
+ continue;
+ ::close(fd);
+ }
+
+ exec();
+ assert(0); // 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;
+}
+
+void SubProcess::exec() {
+ assert(is_child());
+
+ std::vector<const char *> args;
+ args.push_back(cmd.c_str());
+ for (std::vector<std::string>::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::ostringstream err;
+ err << cmd << ": exec failed: " << cpp_strerror(errno) << "\n";
+ write(STDERR_FILENO, err.str().c_str(), err.str().size());
+ _exit(EXIT_FAILURE);
+}
+
+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;
+}
+
+#endif