xref: /openbmc/qemu/system/async-teardown.c (revision 7532ca57)
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 
hup_handler(int signal)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 
async_teardown_fn(void * arg)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  */
new_stack_for_clone(void)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  */
init_async_teardown(void)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