qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

async-teardown.c (3750B)


      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 
     31 static pid_t the_ppid;
     32 
     33 /*
     34  * Close all open file descriptors.
     35  */
     36 static 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 
     67 static 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 
     79 static 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  */
    121 static 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  */
    140 void 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 }