]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: make it easier to reflect child exit status
authorEric Blake <eblake@redhat.com>
Thu, 20 Feb 2014 03:04:40 +0000 (20:04 -0700)
committerEric Blake <eblake@redhat.com>
Mon, 3 Mar 2014 19:40:31 +0000 (12:40 -0700)
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@redhat.com>
src/libvirt_private.syms
src/util/virprocess.c
src/util/virprocess.h
tests/commandtest.c

index 3d8f2e39185252ec891d3983c6820a3356a2a808..97447f64bba74dea31ffc65818af40b1ce513981 100644 (file)
@@ -1679,6 +1679,7 @@ virPortAllocatorRelease;
 
 # util/virprocess.h
 virProcessAbort;
+virProcessExitWithStatus;
 virProcessGetAffinity;
 virProcessGetNamespaces;
 virProcessGetStartTime;
index 305c095106d882dfc6f09a9c6b0b75a10e611871..68c4c148dd3ba55d89c0d5301d11102e7b09153c 100644 (file)
@@ -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);
+}
index 5c173b0302f77e2f4050c61df6d590d7a49b131a..b96dbd4210208e214d50b35ef44377593f552c15 100644 (file)
@@ -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;
index 042f04959d67894f5de89f6f7c82eb4e77e7f944..fcda5e61401cc98c787791c6b0d95f6594630713 100644 (file)
@@ -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) {