qemu/util/async-teardown.c
<<
>>
Prefs
   1/*
   2 * Asynchronous teardown
   3 *
   4 * Copyright IBM, Corp. 2022
   5 *
   6 * Authors:
   7 *  Claudio Imbrenda <imbrenda@linux.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
  10 * option) any later version.  See the COPYING file in the top-level directory.
  11 *
  12 */
  13#include <stdlib.h>
  14#include <stdio.h>
  15#include <sys/types.h>
  16#include <dirent.h>
  17#include <sys/prctl.h>
  18#include <signal.h>
  19#include <sched.h>
  20#include <unistd.h>
  21
  22#include "qemu/osdep.h"
  23#include "qemu/async-teardown.h"
  24
  25#ifdef _SC_THREAD_STACK_MIN
  26#define CLONE_STACK_SIZE sysconf(_SC_THREAD_STACK_MIN)
  27#else
  28#define CLONE_STACK_SIZE 16384
  29#endif
  30
  31static pid_t the_ppid;
  32
  33/*
  34 * Close all open file descriptors.
  35 */
  36static void close_all_open_fd(void)
  37{
  38    struct dirent *de;
  39    int fd, dfd;
  40    DIR *dir;
  41
  42#ifdef CONFIG_CLOSE_RANGE
  43    int r = close_range(0, ~0U, 0);
  44    if (!r) {
  45        /* Success, no need to try other ways. */
  46        return;
  47    }
  48#endif
  49
  50    dir = opendir("/proc/self/fd");
  51    if (!dir) {
  52        /* If /proc is not mounted, there is nothing that can be done. */
  53        return;
  54    }
  55    /* Avoid closing the directory. */
  56    dfd = dirfd(dir);
  57
  58    for (de = readdir(dir); de; de = readdir(dir)) {
  59        fd = atoi(de->d_name);
  60        if (fd != dfd) {
  61            close(fd);
  62        }
  63    }
  64    closedir(dir);
  65}
  66
  67static void hup_handler(int signal)
  68{
  69    /* Check every second if this process has been reparented. */
  70    while (the_ppid == getppid()) {
  71        /* sleep() is safe to use in a signal handler. */
  72        sleep(1);
  73    }
  74
  75    /* At this point the parent process has terminated completely. */
  76    _exit(0);
  77}
  78
  79static int async_teardown_fn(void *arg)
  80{
  81    struct sigaction sa = { .sa_handler = hup_handler };
  82    sigset_t hup_signal;
  83    char name[16];
  84
  85    /* Set a meaningful name for this process. */
  86    snprintf(name, 16, "cleanup/%d", the_ppid);
  87    prctl(PR_SET_NAME, (unsigned long)name);
  88
  89    /*
  90     * Close all file descriptors that might have been inherited from the
  91     * main qemu process when doing clone, needed to make libvirt happy.
  92     * Not using close_range for increased compatibility with older kernels.
  93     */
  94    close_all_open_fd();
  95
  96    /* Set up a handler for SIGHUP and unblock SIGHUP. */
  97    sigaction(SIGHUP, &sa, NULL);
  98    sigemptyset(&hup_signal);
  99    sigaddset(&hup_signal, SIGHUP);
 100    sigprocmask(SIG_UNBLOCK, &hup_signal, NULL);
 101
 102    /* Ask to receive SIGHUP when the parent dies. */
 103    prctl(PR_SET_PDEATHSIG, SIGHUP);
 104
 105    /*
 106     * Sleep forever, unless the parent process has already terminated. The
 107     * only interruption can come from the SIGHUP signal, which in normal
 108     * operation is received when the parent process dies.
 109     */
 110    if (the_ppid == getppid()) {
 111        pause();
 112    }
 113
 114    /* At this point the parent process has terminated completely. */
 115    _exit(0);
 116}
 117
 118/*
 119 * Allocate a new stack of a reasonable size, and return a pointer to its top.
 120 */
 121static void *new_stack_for_clone(void)
 122{
 123    size_t stack_size = CLONE_STACK_SIZE;
 124    char *stack_ptr;
 125
 126    /* Allocate a new stack and get a pointer to its top. */
 127    stack_ptr = qemu_alloc_stack(&stack_size);
 128#if !defined(HOST_HPPA)
 129    /* The top is at the end of the area, except on HPPA. */
 130    stack_ptr += stack_size;
 131#endif
 132
 133    return stack_ptr;
 134}
 135
 136/*
 137 * Block all signals, start (clone) a new process sharing the address space
 138 * with qemu (CLONE_VM), then restore signals.
 139 */
 140void init_async_teardown(void)
 141{
 142    sigset_t all_signals, old_signals;
 143
 144    the_ppid = getpid();
 145
 146    sigfillset(&all_signals);
 147    sigprocmask(SIG_BLOCK, &all_signals, &old_signals);
 148    clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL);
 149    sigprocmask(SIG_SETMASK, &old_signals, NULL);
 150}
 151