Thanks to namespaces, we have a couple of places in the code
base that want to reflect a child exit status, including the
ability to detect death by a signal, back to a grandparent.
Best to make it a reusable function.
* src/util/virprocess.h (virProcessExitWithStatus): New prototype.
* src/libvirt_private.syms (util/virprocess.h): Export it.
* src/util/virprocess.c (virProcessExitWithStatus): New function.
* tests/commandtest.c (test23): Test it.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/virprocess.c | 41 ++++++++++++++++++++++++++++++-
src/util/virprocess.h | 4 ++-
tests/commandtest.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+), 2 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ec786e4..d7a9ee7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1673,6 +1673,7 @@ virPortAllocatorRelease;
# util/virprocess.h
virProcessAbort;
+virProcessExitWithStatus;
virProcessGetAffinity;
virProcessGetNamespaces;
virProcessGetStartTime;
diff --git a/src/util/virprocess.c b/src/util/virprocess.c
index 305c095..68c4c14 100644
--- a/src/util/virprocess.c
+++ b/src/util/virprocess.c
@@ -1,7 +1,7 @@
/*
* virprocess.c: interaction with processes
*
- * Copyright (C) 2010-2013 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
+#include <stdlib.h>
#include <sys/wait.h>
#if HAVE_SETRLIMIT
# include <sys/time.h>
@@ -983,3 +984,41 @@ virProcessRunInMountNamespace(pid_t pid ATTRIBUTE_UNUSED,
return -1;
}
#endif
+
+
+/**
+ * virProcessExitWithStatus:
+ * @status: raw status to be reproduced when this process dies
+ *
+ * Given a raw status obtained by waitpid() or similar, attempt to
+ * make this process exit in the same manner. If the child died by
+ * signal, reset that signal handler to default and raise the same
+ * signal; if that doesn't kill this process, then exit with 128 +
+ * signal number. If @status can't be deciphered, use
+ * EXIT_CANNOT_INVOKE.
+ *
+ * Never returns.
+ */
+void
+virProcessExitWithStatus(int status)
+{
+ int value = EXIT_CANNOT_INVOKE;
+
+ if (WIFEXITED(status)) {
+ value = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ struct sigaction act;
+ sigset_t sigs;
+
+ if (sigemptyset(&sigs) == 0 &&
+ sigaddset(&sigs, WTERMSIG(status)) == 0)
+ sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ sigfillset(&act.sa_mask);
+ sigaction(WTERMSIG(status), &act, NULL);
+ raise(WTERMSIG(status));
+ value = 128 + WTERMSIG(status);
+ }
+ exit(value);
+}
diff --git a/src/util/virprocess.h b/src/util/virprocess.h
index 5c173b0..b96dbd4 100644
--- a/src/util/virprocess.h
+++ b/src/util/virprocess.h
@@ -1,7 +1,7 @@
/*
* virprocess.h: interaction with processes
*
- * Copyright (C) 2010-2013 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -33,6 +33,8 @@ virProcessTranslateStatus(int status);
void
virProcessAbort(pid_t pid);
+void virProcessExitWithStatus(int status) ATTRIBUTE_NORETURN;
+
int
virProcessWait(pid_t pid, int *exitstatus)
ATTRIBUTE_RETURN_CHECK;
diff --git a/tests/commandtest.c b/tests/commandtest.c
index 042f049..fcda5e6 100644
--- a/tests/commandtest.c
+++ b/tests/commandtest.c
@@ -38,6 +38,7 @@
#include "virerror.h"
#include "virthread.h"
#include "virstring.h"
+#include "virprocess.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -937,6 +938,68 @@ cleanup:
return ret;
}
+
+static int
+test23(const void *unused ATTRIBUTE_UNUSED)
+{
+ /* Not strictly a virCommand test, but this is the easiest place
+ * to test this lower-level interface. It takes a double fork to
+ * test virProcessExitWithStatus. */
+ int ret = -1;
+ int status = -1;
+ pid_t pid;
+
+ if (virFork(&pid) < 0)
+ goto cleanup;
+ if (pid < 0)
+ goto cleanup;
+ if (pid == 0) {
+ if (virFork(&pid) < 0)
+ _exit(EXIT_FAILURE);
+ if (pid == 0)
+ _exit(42);
+ if (virProcessWait(pid, &status) < 0)
+ _exit(EXIT_FAILURE);
+ virProcessExitWithStatus(status);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (virProcessWait(pid, &status) < 0)
+ goto cleanup;
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 42) {
+ printf("Unexpected status %d\n", status);
+ goto cleanup;
+ }
+
+ if (virFork(&pid) < 0)
+ goto cleanup;
+ if (pid < 0)
+ goto cleanup;
+ if (pid == 0) {
+ if (virFork(&pid) < 0)
+ _exit(EXIT_FAILURE);
+ if (pid == 0) {
+ raise(SIGKILL);
+ _exit(EXIT_FAILURE);
+ }
+ if (virProcessWait(pid, &status) < 0)
+ _exit(EXIT_FAILURE);
+ virProcessExitWithStatus(status);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (virProcessWait(pid, &status) < 0)
+ goto cleanup;
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
+ printf("Unexpected status %d\n", status);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ return ret;
+}
+
static void virCommandThreadWorker(void *opaque)
{
virCommandTestDataPtr test = opaque;
@@ -1085,6 +1148,7 @@ mymain(void)
DO_TEST(test20);
DO_TEST(test21);
DO_TEST(test22);
+ DO_TEST(test23);
virMutexLock(&test->lock);
if (test->running) {
--
1.8.5.3