[libvirt] [PATCH 03/10] util: make it easier to reflect child exit status

Eric Blake eblake at redhat.com
Thu Feb 20 05:13:16 UTC 2014


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 at 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




More information about the libvir-list mailing list