Browse Source
Libguestfs wants to use qemu to run a captive appliance. When the program linked to libguestfs exits, we want qemu to be cleaned up. Libguestfs goes to great lengths to do this at the moment: it either forks a separate process to ensure clean-up is done, or it asks libvirt to clean up the qemu process. However this is complicated and not totally reliable. On Linux, FreeBSD and macOS, there are mechanisms to ensure a signal or message is delivered to a process when its parent process goes away. The qemu test suite even uses this mechanism on Linux (see PR_SET_PDEATHSIG in tests/qtest/libqtest.c). In nbdkit we have long had the concept of running nbdkit captively, and we have the nbdkit --exit-with-parent flag to help (https://libguestfs.org/nbdkit-captive.1.html#EXIT-WITH-PARENT) This commit adds the same mechanism. The syntax is: qemu -run-with exit-with-parent=on [...] This is not a feature that most typical users of qemu (for running general purpose, long-lived VMs) should use, so it defaults to off. The exit-with-parent.[ch] files are copied from nbdkit, where they have a 3-clause BSD license which is compatible with qemu: https://gitlab.com/nbdkit/nbdkit/-/tree/master/common/utils?ref_type=heads Thanks: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Richard W.M. Jones <rjones@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>pull/307/head
committed by
Daniel P. Berrangé
5 changed files with 222 additions and 2 deletions
@ -0,0 +1,57 @@ |
|||
/*
|
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
* Originally derived from nbdkit common/utils/exit-with-parent.h |
|||
* Copyright Red Hat |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions are |
|||
* met: |
|||
* |
|||
* * Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* |
|||
* * Redistributions in binary form must reproduce the above copyright |
|||
* notice, this list of conditions and the following disclaimer in the |
|||
* documentation and/or other materials provided with the distribution. |
|||
* |
|||
* * Neither the name of Red Hat nor the names of its contributors may be |
|||
* used to endorse or promote products derived from this software without |
|||
* specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND |
|||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR |
|||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
* SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef NBDKIT_EXIT_WITH_PARENT_H |
|||
#define NBDKIT_EXIT_WITH_PARENT_H |
|||
|
|||
/* Test if the feature is available on the platform. */ |
|||
static inline bool can_exit_with_parent(void) |
|||
{ |
|||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) |
|||
return true; |
|||
#else |
|||
return false; |
|||
#endif |
|||
} |
|||
|
|||
/*
|
|||
* --exit-with-parent: kill the current process if the parent exits. |
|||
* This may return -1 on error. |
|||
* |
|||
* Note this will abort on platforms where can_exit_with_parent() |
|||
* returned false. |
|||
*/ |
|||
extern int set_exit_with_parent(void); |
|||
|
|||
#endif /* NBDKIT_EXIT_WITH_PARENT_H */ |
|||
@ -0,0 +1,140 @@ |
|||
/*
|
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
* Originally derived from nbdkit common/utils/exit-with-parent.c |
|||
* Copyright Red Hat |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions are |
|||
* met: |
|||
* |
|||
* * Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* |
|||
* * Redistributions in binary form must reproduce the above copyright |
|||
* notice, this list of conditions and the following disclaimer in the |
|||
* documentation and/or other materials provided with the distribution. |
|||
* |
|||
* * Neither the name of Red Hat nor the names of its contributors may be |
|||
* used to endorse or promote products derived from this software without |
|||
* specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND |
|||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR |
|||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
* SUCH DAMAGE. |
|||
*/ |
|||
|
|||
/*
|
|||
* Implement the --exit-with-parent feature on operating systems which |
|||
* support it. |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "qemu/exit-with-parent.h" |
|||
|
|||
#if defined(__linux__) |
|||
|
|||
#include <sys/prctl.h> |
|||
|
|||
/*
|
|||
* Send SIGTERM to self when the parent exits. This will cause |
|||
* qemu_system_killed() to be called. |
|||
* |
|||
* PR_SET_PDEATHSIG has been defined since Linux 2.1.57. |
|||
*/ |
|||
int |
|||
set_exit_with_parent(void) |
|||
{ |
|||
return prctl(PR_SET_PDEATHSIG, SIGTERM); |
|||
} |
|||
|
|||
#elif defined(__FreeBSD__) |
|||
|
|||
#include <sys/procctl.h> |
|||
|
|||
/*
|
|||
* Send SIGTERM to self when the parent exits. This will cause |
|||
* qemu_system_killed() to be called. |
|||
* |
|||
* PROC_PDEATHSIG_CTL has been defined since FreeBSD 11.2. |
|||
*/ |
|||
int |
|||
set_exit_with_parent(void) |
|||
{ |
|||
const int sig = SIGTERM; |
|||
return procctl(P_PID, 0, PROC_PDEATHSIG_CTL, (void *) &sig); |
|||
} |
|||
|
|||
#elif defined(__APPLE__) |
|||
|
|||
/* For macOS. */ |
|||
|
|||
#include "qemu/thread.h" |
|||
#include "qemu/error-report.h" |
|||
#include "system/runstate.h" |
|||
#include <sys/event.h> |
|||
|
|||
static void * |
|||
exit_with_parent_loop(void *vp) |
|||
{ |
|||
const pid_t ppid = getppid(); |
|||
int fd; |
|||
struct kevent kev, res[1]; |
|||
int r; |
|||
|
|||
/* Register the kevent to wait for ppid to exit. */ |
|||
fd = kqueue(); |
|||
if (fd == -1) { |
|||
error_report("exit_with_parent_loop: kqueue: %m"); |
|||
return NULL; |
|||
} |
|||
EV_SET(&kev, ppid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, NULL); |
|||
if (kevent(fd, &kev, 1, NULL, 0, NULL) == -1) { |
|||
error_report("exit_with_parent_loop: kevent: %m"); |
|||
close(fd); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Wait for the kevent to happen. */ |
|||
r = kevent(fd, 0, 0, res, 1, NULL); |
|||
if (r == 1 && res[0].ident == ppid) { |
|||
/* Behave like Linux and FreeBSD above, as if SIGTERM was sent */ |
|||
qemu_system_killed(SIGTERM, ppid); |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
int |
|||
set_exit_with_parent(void) |
|||
{ |
|||
QemuThread exit_with_parent_thread; |
|||
|
|||
/*
|
|||
* We have to block waiting for kevent, so that requires that we |
|||
* start a background thread. |
|||
*/ |
|||
qemu_thread_create(&exit_with_parent_thread, |
|||
"exit-parent", |
|||
exit_with_parent_loop, NULL, |
|||
QEMU_THREAD_DETACHED); |
|||
return 0; |
|||
} |
|||
|
|||
#else /* any platform that doesn't support this function */ |
|||
|
|||
int |
|||
set_exit_with_parent(void) |
|||
{ |
|||
g_assert_not_reached(); |
|||
} |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue