mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
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:
parent
631923e7f2
commit
2b4f162eb4
@ -1679,6 +1679,7 @@ virPortAllocatorRelease;
|
||||
|
||||
# util/virprocess.h
|
||||
virProcessAbort;
|
||||
virProcessExitWithStatus;
|
||||
virProcessGetAffinity;
|
||||
virProcessGetNamespaces;
|
||||
virProcessGetStartTime;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user