xref: /openbmc/qemu/system/async-teardown.c (revision 90bb6d67)
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 /*
30  * Close all open file descriptors.
31  */
32 static void close_all_open_fd(void)
33 {
34     struct dirent *de;
35     int fd, dfd;
36     DIR *dir;
37 
38 #ifdef CONFIG_CLOSE_RANGE
39     int r = close_range(0, ~0U, 0);
40     if (!r) {
41         /* Success, no need to try other ways. */
42         return;
43     }
44 #endif
45 
46     dir = opendir("/proc/self/fd");
47     if (!dir) {
48         /* If /proc is not mounted, there is nothing that can be done. */
49         return;
50     }
51     /* Avoid closing the directory. */
52     dfd = dirfd(dir);
53 
54     for (de = readdir(dir); de; de = readdir(dir)) {
55         fd = atoi(de->d_name);
56         if (fd != dfd) {
57             close(fd);
58         }
59     }
60     closedir(dir);
61 }
62 
63 static void hup_handler(int signal)
64 {
65     /* Check every second if this process has been reparented. */
66     while (the_ppid == getppid()) {
67         /* sleep() is safe to use in a signal handler. */
68         sleep(1);
69     }
70 
71     /* At this point the parent process has terminated completely. */
72     _exit(0);
73 }
74 
75 static int async_teardown_fn(void *arg)
76 {
77     struct sigaction sa = { .sa_handler = hup_handler };
78     sigset_t hup_signal;
79     char name[16];
80 
81     /* Set a meaningful name for this process. */
82     snprintf(name, 16, "cleanup/%d", the_ppid);
83     prctl(PR_SET_NAME, (unsigned long)name);
84 
85     /*
86      * Close all file descriptors that might have been inherited from the
87      * main qemu process when doing clone, needed to make libvirt happy.
88      * Not using close_range for increased compatibility with older kernels.
89      */
90     close_all_open_fd();
91 
92     /* Set up a handler for SIGHUP and unblock SIGHUP. */
93     sigaction(SIGHUP, &sa, NULL);
94     sigemptyset(&hup_signal);
95     sigaddset(&hup_signal, SIGHUP);
96     sigprocmask(SIG_UNBLOCK, &hup_signal, NULL);
97 
98     /* Ask to receive SIGHUP when the parent dies. */
99     prctl(PR_SET_PDEATHSIG, SIGHUP);
100 
101     /*
102      * Sleep forever, unless the parent process has already terminated. The
103      * only interruption can come from the SIGHUP signal, which in normal
104      * operation is received when the parent process dies.
105      */
106     if (the_ppid == getppid()) {
107         pause();
108     }
109 
110     /* At this point the parent process has terminated completely. */
111     _exit(0);
112 }
113 
114 /*
115  * Allocate a new stack of a reasonable size, and return a pointer to its top.
116  */
117 static void *new_stack_for_clone(void)
118 {
119     size_t stack_size = CLONE_STACK_SIZE;
120     char *stack_ptr;
121 
122     /* Allocate a new stack and get a pointer to its top. */
123     stack_ptr = qemu_alloc_stack(&stack_size);
124     stack_ptr += stack_size;
125 
126     return stack_ptr;
127 }
128 
129 /*
130  * Block all signals, start (clone) a new process sharing the address space
131  * with qemu (CLONE_VM), then restore signals.
132  */
133 void init_async_teardown(void)
134 {
135     sigset_t all_signals, old_signals;
136 
137     the_ppid = getpid();
138 
139     sigfillset(&all_signals);
140     sigprocmask(SIG_BLOCK, &all_signals, &old_signals);
141     clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL);
142     sigprocmask(SIG_SETMASK, &old_signals, NULL);
143 }
144