| /* | 
 |  * Asynchronous teardown | 
 |  * | 
 |  * Copyright IBM, Corp. 2022 | 
 |  * | 
 |  * Authors: | 
 |  *  Claudio Imbrenda <imbrenda@linux.ibm.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU GPL, version 2 or (at your | 
 |  * option) any later version.  See the COPYING file in the top-level directory. | 
 |  * | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include "qemu/config-file.h" | 
 | #include "qemu/option.h" | 
 | #include "qemu/module.h" | 
 | #include <dirent.h> | 
 | #include <sys/prctl.h> | 
 | #include <sched.h> | 
 |  | 
 | #include "qemu/async-teardown.h" | 
 |  | 
 | #ifdef _SC_THREAD_STACK_MIN | 
 | #define CLONE_STACK_SIZE sysconf(_SC_THREAD_STACK_MIN) | 
 | #else | 
 | #define CLONE_STACK_SIZE 16384 | 
 | #endif | 
 |  | 
 | static pid_t the_ppid; | 
 |  | 
 | /* | 
 |  * Close all open file descriptors. | 
 |  */ | 
 | static void close_all_open_fd(void) | 
 | { | 
 |     struct dirent *de; | 
 |     int fd, dfd; | 
 |     DIR *dir; | 
 |  | 
 | #ifdef CONFIG_CLOSE_RANGE | 
 |     int r = close_range(0, ~0U, 0); | 
 |     if (!r) { | 
 |         /* Success, no need to try other ways. */ | 
 |         return; | 
 |     } | 
 | #endif | 
 |  | 
 |     dir = opendir("/proc/self/fd"); | 
 |     if (!dir) { | 
 |         /* If /proc is not mounted, there is nothing that can be done. */ | 
 |         return; | 
 |     } | 
 |     /* Avoid closing the directory. */ | 
 |     dfd = dirfd(dir); | 
 |  | 
 |     for (de = readdir(dir); de; de = readdir(dir)) { | 
 |         fd = atoi(de->d_name); | 
 |         if (fd != dfd) { | 
 |             close(fd); | 
 |         } | 
 |     } | 
 |     closedir(dir); | 
 | } | 
 |  | 
 | static void hup_handler(int signal) | 
 | { | 
 |     /* Check every second if this process has been reparented. */ | 
 |     while (the_ppid == getppid()) { | 
 |         /* sleep() is safe to use in a signal handler. */ | 
 |         sleep(1); | 
 |     } | 
 |  | 
 |     /* At this point the parent process has terminated completely. */ | 
 |     _exit(0); | 
 | } | 
 |  | 
 | static int async_teardown_fn(void *arg) | 
 | { | 
 |     struct sigaction sa = { .sa_handler = hup_handler }; | 
 |     sigset_t hup_signal; | 
 |     char name[16]; | 
 |  | 
 |     /* Set a meaningful name for this process. */ | 
 |     snprintf(name, 16, "cleanup/%d", the_ppid); | 
 |     prctl(PR_SET_NAME, (unsigned long)name); | 
 |  | 
 |     /* | 
 |      * Close all file descriptors that might have been inherited from the | 
 |      * main qemu process when doing clone, needed to make libvirt happy. | 
 |      * Not using close_range for increased compatibility with older kernels. | 
 |      */ | 
 |     close_all_open_fd(); | 
 |  | 
 |     /* Set up a handler for SIGHUP and unblock SIGHUP. */ | 
 |     sigaction(SIGHUP, &sa, NULL); | 
 |     sigemptyset(&hup_signal); | 
 |     sigaddset(&hup_signal, SIGHUP); | 
 |     sigprocmask(SIG_UNBLOCK, &hup_signal, NULL); | 
 |  | 
 |     /* Ask to receive SIGHUP when the parent dies. */ | 
 |     prctl(PR_SET_PDEATHSIG, SIGHUP); | 
 |  | 
 |     /* | 
 |      * Sleep forever, unless the parent process has already terminated. The | 
 |      * only interruption can come from the SIGHUP signal, which in normal | 
 |      * operation is received when the parent process dies. | 
 |      */ | 
 |     if (the_ppid == getppid()) { | 
 |         pause(); | 
 |     } | 
 |  | 
 |     /* At this point the parent process has terminated completely. */ | 
 |     _exit(0); | 
 | } | 
 |  | 
 | /* | 
 |  * Allocate a new stack of a reasonable size, and return a pointer to its top. | 
 |  */ | 
 | static void *new_stack_for_clone(void) | 
 | { | 
 |     size_t stack_size = CLONE_STACK_SIZE; | 
 |     char *stack_ptr; | 
 |  | 
 |     /* Allocate a new stack and get a pointer to its top. */ | 
 |     stack_ptr = qemu_alloc_stack(&stack_size); | 
 | #if !defined(HOST_HPPA) | 
 |     /* The top is at the end of the area, except on HPPA. */ | 
 |     stack_ptr += stack_size; | 
 | #endif | 
 |  | 
 |     return stack_ptr; | 
 | } | 
 |  | 
 | /* | 
 |  * Block all signals, start (clone) a new process sharing the address space | 
 |  * with qemu (CLONE_VM), then restore signals. | 
 |  */ | 
 | void init_async_teardown(void) | 
 | { | 
 |     sigset_t all_signals, old_signals; | 
 |  | 
 |     the_ppid = getpid(); | 
 |  | 
 |     sigfillset(&all_signals); | 
 |     sigprocmask(SIG_BLOCK, &all_signals, &old_signals); | 
 |     clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL); | 
 |     sigprocmask(SIG_SETMASK, &old_signals, NULL); | 
 | } | 
 |  | 
 | static QemuOptsList qemu_run_with_opts = { | 
 |     .name = "run-with", | 
 |     .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head), | 
 |     .desc = { | 
 |         { | 
 |             .name = "async-teardown", | 
 |             .type = QEMU_OPT_BOOL, | 
 |         }, | 
 |         { /* end of list */ } | 
 |     }, | 
 | }; | 
 |  | 
 | static void register_teardown(void) | 
 | { | 
 |     qemu_add_opts(&qemu_run_with_opts); | 
 | } | 
 | opts_init(register_teardown); |