ctdb-common: Add run_proc abstraction
authorAmitay Isaacs <amitay@gmail.com>
Tue, 30 Aug 2016 07:33:42 +0000 (17:33 +1000)
committerAmitay Isaacs <amitay@samba.org>
Sun, 18 Dec 2016 13:23:22 +0000 (14:23 +0100)
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
ctdb/common/run_proc.c [new file with mode: 0644]
ctdb/common/run_proc.h [new file with mode: 0644]
ctdb/tests/cunit/run_proc_001.sh [new file with mode: 0755]
ctdb/tests/src/run_proc_test.c [new file with mode: 0644]
ctdb/wscript

diff --git a/ctdb/common/run_proc.c b/ctdb/common/run_proc.c
new file mode 100644 (file)
index 0000000..96e422c
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+   Run a child process and collect the output
+
+   Copyright (C) Amitay Isaacs  2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/wait.h"
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/tevent_unix.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/blocking.h"
+
+#include "common/db_hash.h"
+#include "common/run_proc.h"
+
+/*
+ * Process abstraction
+ */
+
+struct proc_context {
+       pid_t pid;
+
+       int fd;
+       struct tevent_fd *fde;
+
+       char *output;
+       struct run_proc_result result;
+
+       struct tevent_req *req;
+};
+
+static struct proc_context *proc_new(TALLOC_CTX *mem_ctx)
+{
+       struct proc_context *proc;
+
+       proc = talloc_zero(mem_ctx, struct proc_context);
+       if (proc == NULL) {
+               return NULL;
+       }
+
+       proc->pid = -1;
+       proc->fd = -1;
+
+       return proc;
+}
+
+static void proc_read_handler(struct tevent_context *ev,
+                             struct tevent_fd *fde, uint16_t flags,
+                             void *private_data);
+
+static int proc_start(struct proc_context *proc, struct tevent_context *ev,
+                     const char *path, const char **argv)
+{
+       int fd[2];
+       int ret;
+
+       ret = pipe(fd);
+       if (ret != 0) {
+               return ret;
+       }
+
+       proc->pid = fork();
+       if (proc->pid == -1) {
+               ret = errno;
+               close(fd[0]);
+               close(fd[1]);
+               return ret;
+       }
+
+       if (proc->pid == 0) {
+               close(fd[0]);
+
+               ret = dup2(fd[1], STDOUT_FILENO);
+               if (ret == -1) {
+                       exit(64 + errno);
+               }
+               ret = dup2(fd[1], STDERR_FILENO);
+               if (ret == -1) {
+                       exit(64 + errno);
+               }
+
+               close(fd[1]);
+
+               ret = setpgid(0, 0);
+               if (ret != 0) {
+                       exit(64 + errno);
+               }
+
+               ret = execv(path, discard_const(argv));
+               if (ret != 0) {
+                       exit(64 + errno);
+               }
+
+               exit(64 + ENOEXEC);
+       }
+
+       close(fd[1]);
+
+       proc->fd = fd[0];
+       proc->fde = tevent_add_fd(ev, proc, fd[0], TEVENT_FD_READ,
+                                 proc_read_handler, proc);
+       if (proc->fde == NULL) {
+               return ENOMEM;
+       }
+
+       tevent_fd_set_auto_close(proc->fde);
+
+       return 0;
+}
+
+static void proc_read_handler(struct tevent_context *ev,
+                             struct tevent_fd *fde, uint16_t flags,
+                             void *private_data)
+{
+       struct proc_context *proc = talloc_get_type_abort(
+               private_data, struct proc_context);
+       size_t offset, len = 0;
+       ssize_t nread;
+       int ret;
+
+       ret = ioctl(proc->fd, FIONREAD, &len);
+       if (ret != 0) {
+               goto fail;
+       }
+
+       if (len == 0) {
+               /* pipe closed */
+               goto close;
+       }
+
+       offset = (proc->output == NULL) ? 0 : strlen(proc->output);
+
+       proc->output = talloc_realloc(proc, proc->output, char, offset+len+1);
+       if (proc->output == NULL) {
+               goto fail;
+       }
+
+       nread = sys_read(proc->fd, proc->output + offset, len);
+       if (nread == -1) {
+               goto fail;
+       }
+       proc->output[offset+nread] = '\0';
+       return;
+
+fail:
+       kill(-proc->pid, SIGKILL);
+close:
+       TALLOC_FREE(proc->fde);
+       proc->fd = -1;
+}
+
+/*
+ * Processes database
+ */
+
+static int proc_db_init(TALLOC_CTX *mem_ctx, struct db_hash_context **result)
+{
+       struct db_hash_context *pdb = NULL;
+       int ret;
+
+       ret = db_hash_init(pdb, "proc_db", 1001, DB_HASH_COMPLEX, &pdb);
+       if (ret != 0) {
+               return ret;
+       }
+
+       *result = pdb;
+       return 0;
+}
+
+static int proc_db_add(struct db_hash_context *pdb, pid_t pid,
+                      struct proc_context *proc)
+{
+       return db_hash_insert(pdb, (uint8_t *)&pid, sizeof(pid_t),
+                             (uint8_t *)&proc, sizeof(struct proc_context *));
+}
+
+static int proc_db_remove(struct db_hash_context *pdb, pid_t pid)
+{
+       return db_hash_delete(pdb, (uint8_t *)&pid, sizeof(pid_t));
+}
+
+static int proc_db_fetch_parser(uint8_t *keybuf, size_t keylen,
+                               uint8_t *databuf, size_t datalen,
+                               void *private_data)
+{
+       struct proc_context **result = (struct proc_context **)private_data;
+
+       if (datalen != sizeof(struct proc_context *)) {
+               return EINVAL;
+       }
+
+       *result = *(struct proc_context **)databuf;
+       return 0;
+}
+
+static int proc_db_fetch(struct db_hash_context *pdb, pid_t pid,
+                        struct proc_context **result)
+{
+       return db_hash_fetch(pdb, (uint8_t *)&pid, sizeof(pid_t),
+                            proc_db_fetch_parser, result);
+}
+
+static int proc_db_killall_parser(uint8_t *keybuf, size_t keylen,
+                                 uint8_t *databuf, size_t datalen,
+                                 void *private_data)
+{
+       struct db_hash_context *pdb = talloc_get_type_abort(
+               private_data, struct db_hash_context);
+       struct proc_context *proc;
+       pid_t pid;
+
+       if (keylen != sizeof(pid_t) ||
+           datalen != sizeof(struct proc_context *)) {
+               /* skip */
+               return 0;
+       }
+
+       pid = *(pid_t *)keybuf;
+       proc = talloc_steal(pdb, *(struct proc_context **)databuf);
+
+       TALLOC_FREE(proc->req);
+       TALLOC_FREE(proc->fde);
+
+       kill(-pid, SIGKILL);
+       return 0;
+}
+
+static void proc_db_killall(struct db_hash_context *pdb)
+{
+       (void) db_hash_traverse(pdb, proc_db_killall_parser, pdb, NULL);
+}
+
+
+/*
+ * Run proc abstraction
+ */
+
+struct run_proc_context {
+       struct tevent_context *ev;
+       struct tevent_signal *se;
+       struct db_hash_context *pdb;
+};
+
+static void run_proc_signal_handler(struct tevent_context *ev,
+                                   struct tevent_signal *se,
+                                   int signum, int count, void *siginfo,
+                                   void *private_data);
+static int run_proc_context_destructor(struct run_proc_context *run_ctx);
+static void run_proc_done(struct tevent_req *req);
+
+int run_proc_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                 struct run_proc_context **result)
+{
+       struct run_proc_context *run_ctx;
+       int ret;
+
+       run_ctx = talloc_zero(mem_ctx, struct run_proc_context);
+       if (run_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       run_ctx->ev = ev;
+       run_ctx->se = tevent_add_signal(ev, run_ctx, SIGCHLD, 0,
+                                      run_proc_signal_handler, run_ctx);
+       if (run_ctx->se == NULL) {
+               talloc_free(run_ctx);
+               return ENOMEM;
+       }
+
+       ret = proc_db_init(run_ctx, &run_ctx->pdb);
+       if (ret != 0) {
+               talloc_free(run_ctx);
+               return ret;
+       }
+
+       talloc_set_destructor(run_ctx, run_proc_context_destructor);
+
+       *result = run_ctx;
+       return 0;
+}
+
+static void run_proc_signal_handler(struct tevent_context *ev,
+                                   struct tevent_signal *se,
+                                   int signum, int count, void *siginfo,
+                                   void *private_data)
+{
+       struct run_proc_context *run_ctx = talloc_get_type_abort(
+               private_data, struct run_proc_context);
+       struct proc_context *proc;
+       pid_t pid = -1;
+       int ret, status;
+
+again:
+       pid = waitpid(-1, &status, WNOHANG);
+       if (pid == -1) {
+               return;
+       }
+
+       if (pid == 0) {
+               return;
+       }
+
+       ret = proc_db_fetch(run_ctx->pdb, pid, &proc);
+       if (ret != 0) {
+               /* unknown process */
+               return;
+       }
+
+       /* Mark the process as terminated */
+       proc->pid = -1;
+
+       /* Update process status */
+       if (WIFEXITED(status)) {
+               int pstatus = WEXITSTATUS(status);
+               if (WIFSIGNALED(status)) {
+                       proc->result.sig = WTERMSIG(status);
+               } else if (pstatus >= 64 && pstatus < 255) {
+                       proc->result.err = pstatus-64;
+               } else {
+                       proc->result.status = pstatus;
+               }
+       } else if (WIFSIGNALED(status)) {
+               proc->result.sig = WTERMSIG(status);
+       }
+
+       /* Active run_proc request */
+       if (proc->req != NULL) {
+               run_proc_done(proc->req);
+       }
+
+       proc_db_remove(run_ctx->pdb, pid);
+       talloc_free(proc);
+
+       goto again;
+
+}
+
+static int run_proc_context_destructor(struct run_proc_context *run_ctx)
+{
+       /* Get rid of signal handler */
+       TALLOC_FREE(run_ctx->se);
+
+       /* Kill any pending processes */
+       proc_db_killall(run_ctx->pdb);
+       TALLOC_FREE(run_ctx->pdb);
+
+       return 0;
+}
+
+struct run_proc_state {
+       struct tevent_context *ev;
+       struct proc_context *proc;
+
+       struct run_proc_result result;
+       char *output;
+       pid_t pid;
+};
+
+static int run_proc_state_destructor(struct run_proc_state *state);
+static void run_proc_timedout(struct tevent_req *subreq);
+
+struct tevent_req *run_proc_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct run_proc_context *run_ctx,
+                                const char *path, const char **argv,
+                                struct timeval timeout)
+{
+       struct tevent_req *req;
+       struct run_proc_state *state;
+       struct stat st;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state, struct run_proc_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->pid = -1;
+
+       ret = stat(path, &st);
+       if (ret != 0) {
+               state->result.err = errno;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       if (! (st.st_mode & S_IXUSR)) {
+               state->result.err = EACCES;
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       state->proc = proc_new(run_ctx);
+       if (tevent_req_nomem(state->proc, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       ret = proc_start(state->proc, ev, path, argv);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       state->proc->req = req;
+       talloc_set_destructor(state, run_proc_state_destructor);
+
+       ret = proc_db_add(run_ctx->pdb, state->proc->pid, state->proc);
+       if (ret != 0) {
+               tevent_req_error(req, ret);
+               return tevent_req_post(req, ev);
+       }
+
+       if (! tevent_timeval_is_zero(&timeout)) {
+               struct tevent_req *subreq;
+
+               subreq = tevent_wakeup_send(state, ev, timeout);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, run_proc_timedout, req);
+       }
+
+       return req;
+}
+
+static int run_proc_state_destructor(struct run_proc_state *state)
+{
+       /* Do not get rid of the child process if timeout has occurred */
+       if (state->proc->req != NULL) {
+               state->proc->req = NULL;
+               if (state->proc->pid != -1) {
+                       kill(-state->proc->pid, SIGTERM);
+               }
+       }
+
+       return 0;
+}
+
+static void run_proc_done(struct tevent_req *req)
+{
+       struct run_proc_state *state = tevent_req_data(
+               req, struct run_proc_state);
+
+       state->proc->req = NULL;
+
+       state->result = state->proc->result;
+       if (state->proc->output != NULL) {
+               state->output = talloc_steal(state, state->proc->output);
+       }
+
+       tevent_req_done(req);
+}
+
+static void run_proc_timedout(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct run_proc_state *state = tevent_req_data(
+               req, struct run_proc_state);
+       bool status;
+
+       state->proc->req = NULL;
+
+       status = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (! status) {
+               tevent_req_error(req, EIO);
+               return;
+       }
+
+       state->result.err = ETIME;
+       if (state->proc->output != NULL) {
+               state->output = talloc_steal(state, state->proc->output);
+       }
+       state->pid = state->proc->pid;
+
+       tevent_req_done(req);
+}
+
+bool run_proc_recv(struct tevent_req *req, int *perr,
+                  struct run_proc_result *result, pid_t *pid,
+                  TALLOC_CTX *mem_ctx, char **output)
+{
+       struct run_proc_state *state = tevent_req_data(
+               req, struct run_proc_state);
+       int ret;
+
+       if (tevent_req_is_unix_error(req, &ret)) {
+               if (perr != NULL) {
+                       *perr = ret;
+               }
+               return false;
+       }
+
+       if (result != NULL) {
+               *result = state->result;
+       }
+
+       if (pid != NULL) {
+               *pid = state->pid;
+       }
+
+       if (output != NULL) {
+               *output = talloc_steal(mem_ctx, state->output);
+       }
+
+       return true;
+}
diff --git a/ctdb/common/run_proc.h b/ctdb/common/run_proc.h
new file mode 100644 (file)
index 0000000..4287347
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+   Run a child process and collect the output
+
+   Copyright (C) Amitay Isaacs  2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_RUN_PROC_H__
+#define __CTDB_RUN_PROC_H__
+
+#include <talloc.h>
+#include <tevent.h>
+
+/**
+ * @file run_proc.h
+ *
+ * @brief Run a process and capture the output
+ *
+ * This abstraction allows to execute scripts with argumunts.
+ */
+
+/**
+ * @brief The run process context
+ */
+struct run_proc_context;
+
+/**
+ * @brief The exit status structure
+ *
+ * If the process is terminated due to a signal, sig is set.
+ * If the process is terminated due to an error, err is set.
+ * If the process terminates normally, status is set.
+ */
+struct run_proc_result {
+       int sig;
+       int err;
+       int status;
+};
+
+/**
+ * @brief Initialize the context for running processes
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] ev Tevent context
+ * @param[out] result New run_proc context
+ * @return 0 on success, errno on error
+ */
+int run_proc_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                 struct run_proc_context **result);
+
+/**
+ * @brief Async computation start to run an executable
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] ev Tevent context
+ * @param[in] run_ctx Run_proc context
+ * @param[in] prog The path to the executable
+ * @param[in] argv Arguments to the executable
+ * @param[in] timeout How long to wait for execution
+ * @return new tevent request, or NULL on failure
+ *
+ * argv must include program name as argv[0] and must be null terminated.
+ */
+struct tevent_req *run_proc_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct run_proc_context *run_ctx,
+                                const char *prog, const char **argv,
+                                struct timeval timeout);
+
+/**
+ * @brief Async computation end to run an executable
+ *
+ * @param[in] req Tevent request
+ * @param[out] perr errno in case of failure
+ * @param[out] result The exit status of the executable
+ * @param[out] pid The pid of the child process (still running)
+ * @param[in] mem_ctx Talloc memory context
+ * @param[out] output The output from the executable (stdio + stderr)
+ * @return true on success, false on failure
+ *
+ * The returned pid is -1 if the process has terminated.
+ */
+bool run_proc_recv(struct tevent_req *req, int *perr,
+                  struct run_proc_result *result, pid_t *pid,
+                  TALLOC_CTX *mem_ctx, char **output);
+
+#endif /* __CTDB_RUN_PROC_H__ */
diff --git a/ctdb/tests/cunit/run_proc_001.sh b/ctdb/tests/cunit/run_proc_001.sh
new file mode 100755 (executable)
index 0000000..e1937a8
--- /dev/null
@@ -0,0 +1,140 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+# Invalid path
+ok <<EOF
+Process exited with error 2
+EOF
+unit_test run_proc_test 0 /a/b/c
+
+# Non-executable path
+prog=$(mktemp --tmpdir="$TEST_VAR_DIR")
+cat > "$prog" <<EOF
+echo hello
+EOF
+
+ok <<EOF
+Process exited with error 13
+EOF
+unit_test run_proc_test 0 "$prog"
+
+# Executable path
+chmod +x "$prog"
+
+ok <<EOF
+Process exited with error 8
+EOF
+unit_test run_proc_test 0 "$prog"
+
+# Capture output
+cat > "$prog" <<EOF
+#!/bin/sh
+echo hello
+EOF
+
+ok <<EOF
+Process exited with status 0
+Output = (hello
+)
+EOF
+unit_test run_proc_test 0 "$prog"
+
+# Specify timeout
+ok <<EOF
+Process exited with status 0
+Output = (hello
+)
+EOF
+unit_test run_proc_test 5 "$prog"
+
+# Redirected output
+output=$(mktemp --tmpdir="$TEST_VAR_DIR")
+cat > "$prog" <<EOF
+#!/bin/sh
+exec >"$output" 2>&1
+echo hello
+EOF
+
+ok <<EOF
+Process exited with status 0
+EOF
+unit_test run_proc_test 0 "$prog"
+
+ok <<EOF
+hello
+EOF
+unit_test cat "$output"
+
+# Exit with error
+cat > "$prog" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+ok <<EOF
+Process exited with status 1
+EOF
+unit_test run_proc_test 0 "$prog"
+
+# Exit with signal
+cat > "$prog" <<EOF
+#!/bin/sh
+kill \$$
+EOF
+
+ok <<EOF
+Process exited with signal 15
+EOF
+unit_test run_proc_test 0 "$prog"
+
+# Exit with timeout
+cat > "$prog" <<EOF
+#!/bin/sh
+echo "Sleeping for 5 seconds"
+sleep 5
+EOF
+
+result_filter ()
+{
+       _pid="[0-9][0-9]*"
+       sed -e "s|= ${_pid}|= PID|"
+}
+
+ok <<EOF
+Process exited with error 62
+Child = PID
+Output = (Sleeping for 5 seconds
+)
+EOF
+unit_test run_proc_test 1 "$prog"
+
+# No zombie processes
+pidfile=$(mktemp --tmpdir="$TEST_VAR_DIR")
+
+cat > "$prog" <<EOF
+#!/bin/sh
+echo \$$ > "$pidfile"
+sleep 10
+EOF
+
+ok <<EOF
+Process exited with error 62
+Child = PID
+EOF
+unit_test run_proc_test 1 "$prog"
+
+result_filter ()
+{
+       _header="  *PID  *TTY  *TIME  *CMD"
+       sed -e "s|^${_header}|HEADER|"
+}
+
+pid=$(cat "$pidfile")
+required_result 1 <<EOF
+HEADER
+EOF
+unit_test ps -p "$pid"
+
+rm -f "$pidfile"
+rm -f "$prog"
diff --git a/ctdb/tests/src/run_proc_test.c b/ctdb/tests/src/run_proc_test.c
new file mode 100644 (file)
index 0000000..6db783d
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+   run_proc test wrapper
+
+   Copyright (C) Amitay Isaacs  2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "common/db_hash.c"
+#include "common/run_proc.c"
+
+int main(int argc, const char **argv)
+{
+       TALLOC_CTX *mem_ctx;
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       struct run_proc_context *run_ctx;
+       struct timeval tv;
+       char *output;
+       struct run_proc_result result;
+       pid_t pid;
+       int timeout, ret;
+       bool status;
+
+       if (argc < 3) {
+               fprintf(stderr, "Usage: %s <timeout> <program> <args>\n",
+                       argv[0]);
+               exit(1);
+       }
+
+       mem_ctx = talloc_new(NULL);
+       if (mem_ctx == NULL) {
+               fprintf(stderr, "talloc_new() failed\n");
+               exit(1);
+       }
+
+       ev = tevent_context_init(mem_ctx);
+       if (ev == NULL) {
+               fprintf(stderr, "tevent_context_init() failed\n");
+               exit(1);
+       }
+
+       timeout = atoi(argv[1]);
+       if (timeout <= 0) {
+               tv = tevent_timeval_zero();
+       } else {
+               tv = tevent_timeval_current_ofs(timeout, 0);
+       }
+
+       ret = run_proc_init(mem_ctx, ev, &run_ctx);
+       if (ret != 0) {
+               fprintf(stderr, "run_proc_init() failed, ret=%d\n", ret);
+               exit(1);
+       }
+
+       req = run_proc_send(mem_ctx, ev, run_ctx, argv[2], &argv[2], tv);
+       if (req == NULL) {
+               fprintf(stderr, "run_proc_send() failed\n");
+               exit(1);
+       }
+
+       tevent_req_poll(req, ev);
+
+       status = run_proc_recv(req, &ret, &result, &pid, mem_ctx, &output);
+       if (! status) {
+               fprintf(stderr, "run_proc_recv() failed, ret=%d\n", ret);
+               exit(1);
+       }
+
+       if (result.sig > 0) {
+               printf("Process exited with signal %d\n", result.sig);
+       } else if (result.err > 0) {
+               printf("Process exited with error %d\n", result.err);
+       } else {
+               printf("Process exited with status %d\n", result.status);
+       }
+
+       if (pid != -1) {
+               printf("Child = %d\n", pid);
+       }
+
+       if (output != NULL) {
+               printf("Output = (%s)\n", output);
+       }
+
+       talloc_free(mem_ctx);
+
+       exit(0);
+}
index e19fa713ded418d18f1977ea491517cd9a8538cf..aa357c5d89ed8b56f4d918297ce51f42a031664a 100644 (file)
@@ -390,7 +390,7 @@ def build(bld):
                                           '''db_hash.c srvid.c reqid.c
                                              pkt_read.c pkt_write.c comm.c
                                              logging.c rb_tree.c tunable.c
-                                             pidfile.c'''),
+                                             pidfile.c run_proc.c'''),
                         deps='''samba-util sys_rw tevent-util
                                 replace talloc tevent tdb''')
 
@@ -714,6 +714,7 @@ def build(bld):
         'protocol_types_test',
         'protocol_client_test',
         'pidfile_test',
+        'run_proc_test',
     ]
 
     for target in ctdb_unit_tests:
@@ -722,7 +723,7 @@ def build(bld):
         bld.SAMBA_BINARY(target,
                          source=src,
                          deps='''talloc tevent tdb tevent-util
-                                 LIBASYNC_REQ samba-util''',
+                                 LIBASYNC_REQ samba-util sys_rw''',
                          install_path='${CTDB_TEST_LIBEXECDIR}')
 
     bld.SAMBA_BINARY('reqid_test',