async-teardown.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. #include "qemu/osdep.h"
  14. #include "qemu/config-file.h"
  15. #include "qemu/option.h"
  16. #include "qemu/module.h"
  17. #include <dirent.h>
  18. #include <sys/prctl.h>
  19. #include <sched.h>
  20. #include "qemu/async-teardown.h"
  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. static pid_t the_ppid;
  27. /*
  28. * Close all open file descriptors.
  29. */
  30. static void close_all_open_fd(void)
  31. {
  32. struct dirent *de;
  33. int fd, dfd;
  34. DIR *dir;
  35. #ifdef CONFIG_CLOSE_RANGE
  36. int r = close_range(0, ~0U, 0);
  37. if (!r) {
  38. /* Success, no need to try other ways. */
  39. return;
  40. }
  41. #endif
  42. dir = opendir("/proc/self/fd");
  43. if (!dir) {
  44. /* If /proc is not mounted, there is nothing that can be done. */
  45. return;
  46. }
  47. /* Avoid closing the directory. */
  48. dfd = dirfd(dir);
  49. for (de = readdir(dir); de; de = readdir(dir)) {
  50. fd = atoi(de->d_name);
  51. if (fd != dfd) {
  52. close(fd);
  53. }
  54. }
  55. closedir(dir);
  56. }
  57. static void hup_handler(int signal)
  58. {
  59. /* Check every second if this process has been reparented. */
  60. while (the_ppid == getppid()) {
  61. /* sleep() is safe to use in a signal handler. */
  62. sleep(1);
  63. }
  64. /* At this point the parent process has terminated completely. */
  65. _exit(0);
  66. }
  67. static int async_teardown_fn(void *arg)
  68. {
  69. struct sigaction sa = { .sa_handler = hup_handler };
  70. sigset_t hup_signal;
  71. char name[16];
  72. /* Set a meaningful name for this process. */
  73. snprintf(name, 16, "cleanup/%d", the_ppid);
  74. prctl(PR_SET_NAME, (unsigned long)name);
  75. /*
  76. * Close all file descriptors that might have been inherited from the
  77. * main qemu process when doing clone, needed to make libvirt happy.
  78. * Not using close_range for increased compatibility with older kernels.
  79. */
  80. close_all_open_fd();
  81. /* Set up a handler for SIGHUP and unblock SIGHUP. */
  82. sigaction(SIGHUP, &sa, NULL);
  83. sigemptyset(&hup_signal);
  84. sigaddset(&hup_signal, SIGHUP);
  85. sigprocmask(SIG_UNBLOCK, &hup_signal, NULL);
  86. /* Ask to receive SIGHUP when the parent dies. */
  87. prctl(PR_SET_PDEATHSIG, SIGHUP);
  88. /*
  89. * Sleep forever, unless the parent process has already terminated. The
  90. * only interruption can come from the SIGHUP signal, which in normal
  91. * operation is received when the parent process dies.
  92. */
  93. if (the_ppid == getppid()) {
  94. pause();
  95. }
  96. /* At this point the parent process has terminated completely. */
  97. _exit(0);
  98. }
  99. /*
  100. * Allocate a new stack of a reasonable size, and return a pointer to its top.
  101. */
  102. static void *new_stack_for_clone(void)
  103. {
  104. size_t stack_size = CLONE_STACK_SIZE;
  105. char *stack_ptr;
  106. /* Allocate a new stack and get a pointer to its top. */
  107. stack_ptr = qemu_alloc_stack(&stack_size);
  108. #if !defined(HOST_HPPA)
  109. /* The top is at the end of the area, except on HPPA. */
  110. stack_ptr += stack_size;
  111. #endif
  112. return stack_ptr;
  113. }
  114. /*
  115. * Block all signals, start (clone) a new process sharing the address space
  116. * with qemu (CLONE_VM), then restore signals.
  117. */
  118. void init_async_teardown(void)
  119. {
  120. sigset_t all_signals, old_signals;
  121. the_ppid = getpid();
  122. sigfillset(&all_signals);
  123. sigprocmask(SIG_BLOCK, &all_signals, &old_signals);
  124. clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL);
  125. sigprocmask(SIG_SETMASK, &old_signals, NULL);
  126. }
  127. static QemuOptsList qemu_run_with_opts = {
  128. .name = "run-with",
  129. .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
  130. .desc = {
  131. {
  132. .name = "async-teardown",
  133. .type = QEMU_OPT_BOOL,
  134. },
  135. { /* end of list */ }
  136. },
  137. };
  138. static void register_teardown(void)
  139. {
  140. qemu_add_opts(&qemu_run_with_opts);
  141. }
  142. opts_init(register_teardown);