 80bd81cadd
			
		
	
	
		80bd81cadd
		
	
	
	
	
		
			
			Add new -run-with option with an async-teardown=on|off parameter. It is
visible in the output of query-command-line-options QMP command, so it
can be discovered and used by libvirt.
The option -async-teardown is now redundant, deprecate it.
Reported-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Fixes: c891c24b1a ("os-posix: asynchronous teardown for shutdown on Linux")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-Id: <20230505120051.36605-2-imbrenda@linux.ibm.com>
[thuth: Add curly braces to fix error with GCC 8.5, fix bug in deprecated.rst]
Signed-off-by: Thomas Huth <thuth@redhat.com>
		
	
			
		
			
				
	
	
		
			168 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Asynchronous teardown
 | |
|  *
 | |
|  * Copyright IBM, Corp. 2022
 | |
|  *
 | |
|  * Authors:
 | |
|  *  Claudio Imbrenda <imbrenda@linux.ibm.com>
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
 | |
|  * option) any later version.  See the COPYING file in the top-level directory.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/config-file.h"
 | |
| #include "qemu/option.h"
 | |
| #include "qemu/module.h"
 | |
| #include <dirent.h>
 | |
| #include <sys/prctl.h>
 | |
| #include <sched.h>
 | |
| 
 | |
| #include "qemu/async-teardown.h"
 | |
| 
 | |
| #ifdef _SC_THREAD_STACK_MIN
 | |
| #define CLONE_STACK_SIZE sysconf(_SC_THREAD_STACK_MIN)
 | |
| #else
 | |
| #define CLONE_STACK_SIZE 16384
 | |
| #endif
 | |
| 
 | |
| static pid_t the_ppid;
 | |
| 
 | |
| /*
 | |
|  * Close all open file descriptors.
 | |
|  */
 | |
| static void close_all_open_fd(void)
 | |
| {
 | |
|     struct dirent *de;
 | |
|     int fd, dfd;
 | |
|     DIR *dir;
 | |
| 
 | |
| #ifdef CONFIG_CLOSE_RANGE
 | |
|     int r = close_range(0, ~0U, 0);
 | |
|     if (!r) {
 | |
|         /* Success, no need to try other ways. */
 | |
|         return;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     dir = opendir("/proc/self/fd");
 | |
|     if (!dir) {
 | |
|         /* If /proc is not mounted, there is nothing that can be done. */
 | |
|         return;
 | |
|     }
 | |
|     /* Avoid closing the directory. */
 | |
|     dfd = dirfd(dir);
 | |
| 
 | |
|     for (de = readdir(dir); de; de = readdir(dir)) {
 | |
|         fd = atoi(de->d_name);
 | |
|         if (fd != dfd) {
 | |
|             close(fd);
 | |
|         }
 | |
|     }
 | |
|     closedir(dir);
 | |
| }
 | |
| 
 | |
| static void hup_handler(int signal)
 | |
| {
 | |
|     /* Check every second if this process has been reparented. */
 | |
|     while (the_ppid == getppid()) {
 | |
|         /* sleep() is safe to use in a signal handler. */
 | |
|         sleep(1);
 | |
|     }
 | |
| 
 | |
|     /* At this point the parent process has terminated completely. */
 | |
|     _exit(0);
 | |
| }
 | |
| 
 | |
| static int async_teardown_fn(void *arg)
 | |
| {
 | |
|     struct sigaction sa = { .sa_handler = hup_handler };
 | |
|     sigset_t hup_signal;
 | |
|     char name[16];
 | |
| 
 | |
|     /* Set a meaningful name for this process. */
 | |
|     snprintf(name, 16, "cleanup/%d", the_ppid);
 | |
|     prctl(PR_SET_NAME, (unsigned long)name);
 | |
| 
 | |
|     /*
 | |
|      * Close all file descriptors that might have been inherited from the
 | |
|      * main qemu process when doing clone, needed to make libvirt happy.
 | |
|      * Not using close_range for increased compatibility with older kernels.
 | |
|      */
 | |
|     close_all_open_fd();
 | |
| 
 | |
|     /* Set up a handler for SIGHUP and unblock SIGHUP. */
 | |
|     sigaction(SIGHUP, &sa, NULL);
 | |
|     sigemptyset(&hup_signal);
 | |
|     sigaddset(&hup_signal, SIGHUP);
 | |
|     sigprocmask(SIG_UNBLOCK, &hup_signal, NULL);
 | |
| 
 | |
|     /* Ask to receive SIGHUP when the parent dies. */
 | |
|     prctl(PR_SET_PDEATHSIG, SIGHUP);
 | |
| 
 | |
|     /*
 | |
|      * Sleep forever, unless the parent process has already terminated. The
 | |
|      * only interruption can come from the SIGHUP signal, which in normal
 | |
|      * operation is received when the parent process dies.
 | |
|      */
 | |
|     if (the_ppid == getppid()) {
 | |
|         pause();
 | |
|     }
 | |
| 
 | |
|     /* At this point the parent process has terminated completely. */
 | |
|     _exit(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Allocate a new stack of a reasonable size, and return a pointer to its top.
 | |
|  */
 | |
| static void *new_stack_for_clone(void)
 | |
| {
 | |
|     size_t stack_size = CLONE_STACK_SIZE;
 | |
|     char *stack_ptr;
 | |
| 
 | |
|     /* Allocate a new stack and get a pointer to its top. */
 | |
|     stack_ptr = qemu_alloc_stack(&stack_size);
 | |
| #if !defined(HOST_HPPA)
 | |
|     /* The top is at the end of the area, except on HPPA. */
 | |
|     stack_ptr += stack_size;
 | |
| #endif
 | |
| 
 | |
|     return stack_ptr;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Block all signals, start (clone) a new process sharing the address space
 | |
|  * with qemu (CLONE_VM), then restore signals.
 | |
|  */
 | |
| void init_async_teardown(void)
 | |
| {
 | |
|     sigset_t all_signals, old_signals;
 | |
| 
 | |
|     the_ppid = getpid();
 | |
| 
 | |
|     sigfillset(&all_signals);
 | |
|     sigprocmask(SIG_BLOCK, &all_signals, &old_signals);
 | |
|     clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL);
 | |
|     sigprocmask(SIG_SETMASK, &old_signals, NULL);
 | |
| }
 | |
| 
 | |
| static QemuOptsList qemu_run_with_opts = {
 | |
|     .name = "run-with",
 | |
|     .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
 | |
|     .desc = {
 | |
|         {
 | |
|             .name = "async-teardown",
 | |
|             .type = QEMU_OPT_BOOL,
 | |
|         },
 | |
|         { /* end of list */ }
 | |
|     },
 | |
| };
 | |
| 
 | |
| static void register_teardown(void)
 | |
| {
 | |
|     qemu_add_opts(&qemu_run_with_opts);
 | |
| }
 | |
| opts_init(register_teardown);
 |