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 }