From 2b4f162eb4ca0f161fbdb66c5814cd02bfa665ca Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 19 Feb 2014 20:04:40 -0700 Subject: [PATCH] util: make it easier to reflect child exit status 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 --- 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 3d8f2e3918..97447f64bb 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1679,6 +1679,7 @@ virPortAllocatorRelease; # util/virprocess.h virProcessAbort; +virProcessExitWithStatus; virProcessGetAffinity; virProcessGetNamespaces; virProcessGetStartTime; diff --git a/src/util/virprocess.c b/src/util/virprocess.c index 305c095106..68c4c148dd 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 #include #include +#include #include #if HAVE_SETRLIMIT # include @@ -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 5c173b0302..b96dbd4210 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 042f04959d..fcda5e6140 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) {