]> Git Repo - qemu.git/blob - util/async-teardown.c
works with less than base ISA qemu-system-riscv32 -M virt -bios none -kernel output...
[qemu.git] / util / async-teardown.c
1 /*
2  * Asynchronous teardown
3  *
4  * Copyright IBM, Corp. 2022
5  *
6  * Authors:
7  *  Claudio Imbrenda <[email protected]>
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 }
This page took 0.059238 seconds and 4 git commands to generate.