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 <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-02-19 20:04:40 -07:00
parent 631923e7f2
commit 2b4f162eb4
4 changed files with 108 additions and 2 deletions

View File

@ -1679,6 +1679,7 @@ virPortAllocatorRelease;
# util/virprocess.h
virProcessAbort;
virProcessExitWithStatus;
virProcessGetAffinity;
virProcessGetNamespaces;
virProcessGetStartTime;

View 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);
}

View 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;

View 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) {