Merge tag 'qemu-macppc-20230206' of https://github.com/mcayland/qemu into staging
[qemu.git] / util / async-teardown.c
blob62bfce1b3ca84055e3a5369334a8e1cb1510557e
1 /*
2 * Asynchronous teardown
4 * Copyright IBM, Corp. 2022
6 * Authors:
7 * Claudio Imbrenda <imbrenda@linux.ibm.com>
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.
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>
22 #include "qemu/osdep.h"
23 #include "qemu/async-teardown.h"
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
31 static pid_t the_ppid;
34 * Close all open file descriptors.
36 static void close_all_open_fd(void)
38 struct dirent *de;
39 int fd, dfd;
40 DIR *dir;
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;
48 #endif
50 dir = opendir("/proc/self/fd");
51 if (!dir) {
52 /* If /proc is not mounted, there is nothing that can be done. */
53 return;
55 /* Avoid closing the directory. */
56 dfd = dirfd(dir);
58 for (de = readdir(dir); de; de = readdir(dir)) {
59 fd = atoi(de->d_name);
60 if (fd != dfd) {
61 close(fd);
64 closedir(dir);
67 static void hup_handler(int signal)
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);
75 /* At this point the parent process has terminated completely. */
76 _exit(0);
79 static int async_teardown_fn(void *arg)
81 struct sigaction sa = { .sa_handler = hup_handler };
82 sigset_t hup_signal;
83 char name[16];
85 /* Set a meaningful name for this process. */
86 snprintf(name, 16, "cleanup/%d", the_ppid);
87 prctl(PR_SET_NAME, (unsigned long)name);
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.
94 close_all_open_fd();
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);
102 /* Ask to receive SIGHUP when the parent dies. */
103 prctl(PR_SET_PDEATHSIG, SIGHUP);
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.
110 if (the_ppid == getppid()) {
111 pause();
114 /* At this point the parent process has terminated completely. */
115 _exit(0);
119 * Allocate a new stack of a reasonable size, and return a pointer to its top.
121 static void *new_stack_for_clone(void)
123 size_t stack_size = CLONE_STACK_SIZE;
124 char *stack_ptr;
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
133 return stack_ptr;
137 * Block all signals, start (clone) a new process sharing the address space
138 * with qemu (CLONE_VM), then restore signals.
140 void init_async_teardown(void)
142 sigset_t all_signals, old_signals;
144 the_ppid = getpid();
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);