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 14 #include "qemu/osdep.h" 15 #include <dirent.h> 16 #include <sys/prctl.h> 17 #include <sched.h> 18 19 #include "qemu/async-teardown.h" 20 21 #ifdef _SC_THREAD_STACK_MIN 22 #define CLONE_STACK_SIZE sysconf(_SC_THREAD_STACK_MIN) 23 #else 24 #define CLONE_STACK_SIZE 16384 25 #endif 26 27 static pid_t the_ppid; 28 29 static void hup_handler(int signal) 30 { 31 /* Check every second if this process has been reparented. */ 32 while (the_ppid == getppid()) { 33 /* sleep() is safe to use in a signal handler. */ 34 sleep(1); 35 } 36 37 /* At this point the parent process has terminated completely. */ 38 _exit(0); 39 } 40 41 static int async_teardown_fn(void *arg) 42 { 43 struct sigaction sa = { .sa_handler = hup_handler }; 44 sigset_t hup_signal; 45 char name[16]; 46 47 /* Set a meaningful name for this process. */ 48 snprintf(name, 16, "cleanup/%d", the_ppid); 49 prctl(PR_SET_NAME, (unsigned long)name); 50 51 /* 52 * Close all file descriptors that might have been inherited from the 53 * main qemu process when doing clone, needed to make libvirt happy. 54 */ 55 qemu_close_all_open_fd(NULL, 0); 56 57 /* Set up a handler for SIGHUP and unblock SIGHUP. */ 58 sigaction(SIGHUP, &sa, NULL); 59 sigemptyset(&hup_signal); 60 sigaddset(&hup_signal, SIGHUP); 61 sigprocmask(SIG_UNBLOCK, &hup_signal, NULL); 62 63 /* Ask to receive SIGHUP when the parent dies. */ 64 prctl(PR_SET_PDEATHSIG, SIGHUP); 65 66 /* 67 * Sleep forever, unless the parent process has already terminated. The 68 * only interruption can come from the SIGHUP signal, which in normal 69 * operation is received when the parent process dies. 70 */ 71 if (the_ppid == getppid()) { 72 pause(); 73 } 74 75 /* At this point the parent process has terminated completely. */ 76 _exit(0); 77 } 78 79 /* 80 * Allocate a new stack of a reasonable size, and return a pointer to its top. 81 */ 82 static void *new_stack_for_clone(void) 83 { 84 size_t stack_size = CLONE_STACK_SIZE; 85 char *stack_ptr; 86 87 /* Allocate a new stack and get a pointer to its top. */ 88 stack_ptr = qemu_alloc_stack(&stack_size); 89 stack_ptr += stack_size; 90 91 return stack_ptr; 92 } 93 94 /* 95 * Block all signals, start (clone) a new process sharing the address space 96 * with qemu (CLONE_VM), then restore signals. 97 */ 98 void init_async_teardown(void) 99 { 100 sigset_t all_signals, old_signals; 101 102 the_ppid = getpid(); 103 104 sigfillset(&all_signals); 105 sigprocmask(SIG_BLOCK, &all_signals, &old_signals); 106 clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL); 107 sigprocmask(SIG_SETMASK, &old_signals, NULL); 108 } 109