]>
Commit | Line | Data |
---|---|---|
c891c24b CI |
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 | */ | |
2ca10fae MA |
13 | |
14 | #include "qemu/osdep.h" | |
c891c24b CI |
15 | #include <dirent.h> |
16 | #include <sys/prctl.h> | |
c891c24b | 17 | #include <sched.h> |
c891c24b | 18 | |
c891c24b CI |
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); | |
c891c24b | 124 | stack_ptr += stack_size; |
c891c24b CI |
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 | } |