From: Petr Tesarik <ptesarik@suse.cz>
Subject: Backport class SubProcess
References: FATE#318874, bsc#917747
Patch-mainline: v0.8.12
Git-commit: 57d74fe910c3c5fd26bfaec8aab38efa84081b18
Git-commit: e8f0572befb9e95e4ce0aca0bbd5e9bd392a26b6
Git-commit: e9ac4ea8287e98156208bb93a1da5af9b752711b
Git-commit: 37952df87b8a7e417accf2563666485c04eec913
Git-commit: a88ecd3da64c47882e3b8dd8256ba8285795a94c
Git-commit: 103cfb0cc3fc78fc6639ce1e03f780fdacd81f18

This is a relatively low-level interface to creating a process,
optionally with input/output redirections.

Backport the code from kdump-0.8.12.

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 kdumptool/process.cc |  246 +++++++++++++++++++++++++++++++++++++++++++++++++++
 kdumptool/process.h  |  194 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 440 insertions(+)

--- a/kdumptool/process.cc
+++ b/kdumptool/process.cc
@@ -36,6 +36,250 @@ using std::string;
 using std::min;
 using std::max;
 
+//{{{ SubProcess ---------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+void SubProcess::PipeInfo::close(void)
+    throw()
+{
+    closeParent();
+    closeChild();
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::PipeInfo::closeParent(void)
+    throw()
+{
+    if (parentfd >= 0) {
+	::close(parentfd);
+	parentfd = -1;
+    }
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::PipeInfo::closeChild(void)
+    throw()
+{
+    if (childfd >= 0) {
+	::close(childfd);
+	childfd = -1;
+    }
+}
+
+// -----------------------------------------------------------------------------
+SubProcess::SubProcess()
+    : m_pid(-1), m_killSignal(SIGKILL)
+{}
+
+// -----------------------------------------------------------------------------
+SubProcess::~SubProcess()
+{
+    _closeParentFDs();
+    if (m_pid != -1) {
+	kill();
+	wait();
+    }
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::checkSpawned(void)
+    throw (KError)
+{
+    if (m_pid == -1)
+	throw KError("SubProcess::checkSpawned(): no subprocess spawned");
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::setPipeDirection(int fd, enum PipeDirection dir)
+{
+    m_redirs.erase(fd);
+
+    if (dir == None)
+	m_pipes.erase(fd);
+    else {
+	std::pair<int, struct PipeInfo> val(fd, PipeInfo(dir));
+	std::pair<std::map<int, struct PipeInfo>::iterator, bool> ret;
+	ret = m_pipes.insert(val);
+	if (ret.second == false) {
+	    ret.first->second.close();
+	    ret.first->second.dir = dir;
+	}
+    }
+}
+
+// -----------------------------------------------------------------------------
+enum SubProcess::PipeDirection SubProcess::getPipeDirection(int fd)
+{
+    std::map<int, struct PipeInfo>::iterator ret;
+    ret = m_pipes.find(fd);
+    return (ret == m_pipes.end())
+	? None
+	: ret->second.dir;
+}
+
+// -----------------------------------------------------------------------------
+int SubProcess::getPipeFD(int fd)
+    throw (std::out_of_range)
+{
+    std::map<int, struct PipeInfo>::iterator ret;
+    ret = m_pipes.find(fd);
+    if (ret == m_pipes.end())
+	throw std::out_of_range("SubProcess::getPipeFD(): Unknown fd "
+				+ Stringutil::number2string(fd));
+    return ret->second.parentfd;
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::setRedirection(int fd, int srcfd)
+{
+    m_pipes.erase(fd);
+
+    if (srcfd < 0)
+	m_redirs.erase(fd);
+    else
+	m_redirs[fd] = srcfd;
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::_closeParentFDs(void)
+{
+    std::map<int, struct PipeInfo>::iterator it;
+    for (it = m_pipes.begin(); it != m_pipes.end(); ++it)
+	it->second.closeParent();
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::_closeChildFDs(void)
+{
+    std::map<int, struct PipeInfo>::iterator it;
+    for (it = m_pipes.begin(); it != m_pipes.end(); ++it)
+	it->second.closeChild();
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::spawn(const string &name, const StringVector &args)
+{
+    Debug::debug()->trace("SubProcess::spawn(%s, %s)",
+        name.c_str(), Stringutil::vector2string(args, ":").c_str());
+
+    //
+    // setup pipes
+    //
+    try {
+	std::map<int, struct PipeInfo>::iterator it;
+	for (it = m_pipes.begin(); it != m_pipes.end(); ++it) {
+	    int pipefd[2];
+
+	    if (it->second.dir != ParentToChild &&
+		it->second.dir != ChildToParent)
+		throw KError("SubProcess::spawn(): "
+			     "invalid pipe direction for fd "
+			     + Stringutil::number2string(it->first) + ": "
+			     + Stringutil::number2string(it->second.dir));
+
+	    if (pipe2(pipefd, O_CLOEXEC) < 0)
+		throw KSystemError("SubProcess::spawn(): "
+				   "cannot create pipe for fd "
+				   + Stringutil::number2string(it->first),
+				   errno);
+
+	    if(it->second.dir == ParentToChild) {
+		it->second.parentfd = pipefd[1];
+		it->second.childfd = pipefd[0];
+	    } else {		// ChildToParent
+		it->second.parentfd = pipefd[0];
+		it->second.childfd = pipefd[1];
+	    }
+	}
+    } catch(...) {
+	_closeParentFDs();
+	_closeChildFDs();
+	throw;
+    }
+
+    //
+    // execute the child
+    //
+
+    pid_t child = fork();
+    if (child > 0) {		// parent code
+	m_pid = child;
+
+	_closeChildFDs();
+
+    } else {
+	_closeParentFDs();
+
+        if (child == 0) {	// child code
+	    std::map<int, struct PipeInfo>::iterator it;
+	    for (it = m_pipes.begin(); it != m_pipes.end(); ++it)
+		dup2(it->second.childfd, it->first);
+
+	    std::map<int, int>::iterator redir;
+	    for (redir = m_redirs.begin(); redir != m_redirs.end(); ++redir)
+		dup2(redir->second, redir->first);
+        }
+
+        if (child != 0) {	// parent code failure
+	    _closeChildFDs();
+            throw KSystemError("SubProcess::spawn(): fork failed", errno);
+	}
+
+	// child code, execute the process
+	StringVector fullV = args;
+	fullV.insert(fullV.begin(), name);
+	char **vector = Stringutil::stringv2charv(fullV);
+
+	int ret = execvp(name.c_str(), vector);
+	Util::freev(vector);
+
+	if (ret < 0)
+	    throw KSystemError("Execution of '" + name + "' failed.", errno);
+    }
+
+    Debug::debug()->dbg("Forked child PID %d", m_pid);
+}
+
+// -----------------------------------------------------------------------------
+void SubProcess::kill(int sig)
+    throw (KError)
+{
+    Debug::debug()->trace("SubProcess::kill(%d)", sig);
+
+    checkSpawned();
+
+    if (::kill(m_pid, sig))
+	throw KSystemError("SubProcess::kill(): cannot send signal"
+			   + Stringutil::number2string(sig), errno);
+}
+
+// -----------------------------------------------------------------------------
+int SubProcess::wait(void)
+    throw (KError)
+{
+    Debug::debug()->trace("SubProcess::wait() on %d", m_pid);
+
+    checkSpawned();
+
+    int status;
+    pid_t ret = ::waitpid(m_pid, &status, 0);
+
+    if (ret == -1)
+	throw KSystemError("SubProcess::wait(): cannot get state of PID "
+			   + Stringutil::number2string(m_pid), errno);
+
+    if (ret != m_pid)
+	throw KError("SubProcess::wait(): spawned PID "
+		     + Stringutil::number2string(m_pid) + " but PID "
+		     + Stringutil::number2string(ret) + " exited.");
+
+    Debug::debug()->dbg("PID %d exited with status 0x%04x", m_pid, status);
+
+    m_pid = -1;
+    return status;
+}
+
+//}}}
 //{{{ MultiplexIO --------------------------------------------------------------
 
 // -----------------------------------------------------------------------------
@@ -357,4 +601,6 @@ void ProcessFilter::executeProcess(const
         throw KSystemError("Execution of '" + name + "' failed.", errno);
 }
 
+//}}}
+
 // vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1:
--- a/kdumptool/process.h
+++ b/kdumptool/process.h
@@ -20,11 +20,205 @@
 #define PROCESS_H
 
 #include <stdint.h>
+#include <map>
 
 #include "global.h"
 #include "optionparser.h"
 #include "subcommand.h"
 
+//{{{ SubProcess ---------------------------------------------------------------
+
+/**
+ * Representation of a subprocess in the parent.
+ */
+class SubProcess {
+
+    public:
+
+	enum PipeDirection {
+		None = -1,
+		ParentToChild,
+		ChildToParent
+	};
+
+	/**
+	 * Prepare a new subprocess.
+	 */
+	SubProcess();
+
+	/**
+	 * Destructor. Kills the subprocess if still running.
+	 */
+	virtual ~SubProcess();
+
+	/**
+	 * Specify how a file descriptor should be redirected.
+	 *
+	 * @param[in] File descriptor in child.
+	 * @param[in] Pipe direction.
+	 *
+	 * Note that pipes and redirections are mutually exclusive, so
+	 * if you set up a pipe, any respective redirection for the same
+	 * file descriptor will be removed.
+	 */
+	void setPipeDirection(int fd, enum PipeDirection dir);
+
+	/**
+	 * Check how a file descriptor should be redirected.
+	 *
+	 * @param[in] File descriptor in child.
+	 */
+	enum PipeDirection getPipeDirection(int fd);
+
+	/**
+	 * Get the file descriptor of the other end of a pipe redirected
+	 * in the child.
+	 *
+	 * @param[in] File descriptor in child.
+	 * @exception out_of_range if the file descriptor is not piped.
+	 */
+	int getPipeFD(int fd)
+	throw (std::out_of_range);
+
+	/**
+	 * Set up redirection from an open file descriptor.
+	 *
+	 * @param[in] fd File descriptor in child.
+	 * @param[in] srcfd File descriptor in parent.
+	 *            Use -1 to remove a redirection.
+	 *
+	 * The source file descriptor is not closed in child. You can
+	 * use the O_CLOEXEC flag to control which file descriptors are
+	 * preserved across exec.
+	 *
+	 * Note that pipes and redirections are mutually exclusive, so
+	 * if you set a redirection, any respective pipe setup for the
+	 * same file descriptor will be removed.
+	 */
+	void setRedirection(int fd, int srcfd);
+
+	/**
+	 * Spawns a subprocess.
+	 *
+         * @param[in] name the executable (PATH is searched) of the process
+         *            to execute
+         * @param[in] args the arguments for the process (argv[0] is used from
+         *            @c name, so it cannot be overwritten here). Pass an empty
+         *            list if you don't want to provide arguments.
+	 */
+	void spawn(const std::string &name, const StringVector &args);
+
+	/**
+	 * Get the child process ID.
+	 *
+	 * @returns system PID, or -1 if none.
+	 */
+	int getChildPID(void)
+	throw ()
+	{ return m_pid; }
+
+	/**
+	 * Set the default kill signal.
+	 *
+	 * @param[in] The signal to kill the child.
+	 */
+	void setKillSignal(int sig)
+	throw ()
+	{ m_killSignal = sig; }
+
+	/**
+	 * Get the default kill signal.
+	 */
+	int getKillSignal(void)
+	throw ()
+	{ return m_killSignal; }
+
+	/**
+	 * Send a signal to the child process.
+	 *
+	 * @param[in] The signal to be sent, see kill(2).
+	 * @exception KError if kill(2) fails.
+	 */
+	void kill(int sig)
+	throw (KError);
+
+	/**
+	 * Send the default kill signal to the child process.
+	 */
+	void kill(void)
+	throw(KError)
+	{ kill(m_killSignal); }
+
+	/**
+	 * Wait for the child to terminate.
+	 *
+	 * @return Child exit status, see wait(2).
+	 */
+	int wait(void)
+	throw(KError);
+
+    protected:
+
+	/**
+	 * Make sure that a child process has been spawned.
+	 *
+	 * @exception KError if there is no child process.
+	 */
+	void checkSpawned(void)
+	throw(KError);
+
+	pid_t m_pid;
+	int m_killSignal;
+
+    private:
+
+	struct PipeInfo {
+	    enum PipeDirection dir;
+	    int parentfd;	/* fd in parent */
+	    int childfd;	/* fd for child (during init) */
+
+	    /**
+	     * Initialize the info
+	     */
+	    PipeInfo(enum PipeDirection adir)
+	    throw ()
+	    : dir(adir), parentfd(-1), childfd(-1)
+	    { }
+
+	    /**
+	     * Destructor.
+	     */
+	    ~PipeInfo()
+	    throw ()
+	    { close(); }
+
+	    /**
+	     * Close all open file descriptors
+	     */
+	    void close(void)
+	    throw ();
+
+	    /**
+	     * Close parent file descriptor, if open
+	     */
+	    void closeParent(void)
+	    throw ();
+
+	    /**
+	     * Close child file descriptor, if open
+	     */
+	    void closeChild(void)
+	    throw ();
+	};
+	std::map<int, struct PipeInfo> m_pipes;
+
+	std::map<int, int> m_redirs;
+
+	void _closeParentFDs(void);
+	void _closeChildFDs(void);
+};
+
+//}}}
 //{{{ MultiplexIO --------------------------------------------------------------
 
 struct pollfd;
