xvfb-wrapper.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright © 2014 Ran Benita <ran234@gmail.com>
  3. * Copyright © 2023 Pierre Le Marre <dev@wismill.eu>
  4. * SPDX-License-Identifier: MIT
  5. */
  6. #include "config.h"
  7. #include "test-config.h"
  8. #include <assert.h>
  9. #include <signal.h>
  10. #include <spawn.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/types.h>
  14. #include <sys/wait.h>
  15. #include "test.h"
  16. #include "xvfb-wrapper.h"
  17. static volatile bool xvfb_is_ready;
  18. static void
  19. sigusr1_handler(int signal)
  20. {
  21. xvfb_is_ready = true;
  22. }
  23. int
  24. xvfb_wrapper(x11_test_func_t test_func, void *private)
  25. {
  26. int ret = EXIT_SUCCESS;
  27. FILE * display_fd;
  28. char display_fd_string[32];
  29. sigset_t mask;
  30. struct sigaction sa;
  31. char *xvfb_argv[] = {
  32. (char *) "Xvfb", (char *) "-displayfd", display_fd_string, NULL
  33. };
  34. char *envp[] = { NULL };
  35. pid_t xvfb_pid = 0;
  36. size_t counter = 0;
  37. char display[32] = ":";
  38. size_t length;
  39. /* File descriptor to retrieve the display number */
  40. display_fd = tmpfile();
  41. if (display_fd == NULL){
  42. fprintf(stderr, "Unable to create temporary file.\n");
  43. goto err_display_fd;
  44. }
  45. snprintf(display_fd_string, sizeof(display_fd_string), "%d", fileno(display_fd));
  46. /* Set SIGUSR1 to SIG_IGN so Xvfb will send us that signal when it's ready
  47. * to accept connections. In order to avoid race condition, we block the
  48. * until we are ready to process it. */
  49. sigemptyset(&mask);
  50. sigaddset(&mask, SIGUSR1);
  51. sigprocmask(SIG_BLOCK, &mask, NULL);
  52. sa.sa_handler = sigusr1_handler;
  53. sa.sa_flags = SA_RESTART;
  54. sigemptyset(&sa.sa_mask);
  55. struct sigaction sa_old;
  56. sigaction(SIGUSR1, &sa, &sa_old);
  57. xvfb_is_ready = false;
  58. /*
  59. * Xvfb command: let the server find an available display.
  60. *
  61. * Note that it may generate multiple times the following output in stderr:
  62. * _XSERVTransSocketUNIXCreateListener: ...SocketCreateListener() failed
  63. * It is expected: this is the server trying the ports until it finds one
  64. * that works.
  65. */
  66. ret = posix_spawnp(&xvfb_pid, "Xvfb", NULL, NULL, xvfb_argv, envp);
  67. if (ret != 0) {
  68. fprintf(stderr,
  69. "[ERROR] Cannot run Xvfb. posix_spawnp error %d: %s\n",
  70. ret, strerror(ret));
  71. if (ret == ENOENT) {
  72. fprintf(stderr,
  73. "[ERROR] Xvfb may be missing. "
  74. "Please install the corresponding package, "
  75. "e.g. \"xvfb\" or \"xorg-x11-server-Xvfb\".\n");
  76. }
  77. ret = TEST_SETUP_FAILURE;
  78. goto err_xvfd;
  79. }
  80. sigprocmask(SIG_UNBLOCK, &mask, NULL);
  81. /* Now wait for the SIGUSR1 signal that Xvfb is ready */
  82. while (!xvfb_is_ready) {
  83. usleep(1000);
  84. if (++counter >= 3000) /* 3 seconds max wait */
  85. break;
  86. }
  87. sigaction(SIGUSR1, &sa_old, NULL);
  88. /* Check if Xvfb is still alive */
  89. pid_t pid = waitpid(xvfb_pid, NULL, WNOHANG);
  90. if (pid != 0) {
  91. fprintf(stderr, "ERROR: Xvfb not alive\n");
  92. ret = TEST_SETUP_FAILURE;
  93. goto err_xvfd;
  94. }
  95. /* Retrieve the display number: Xvfd writes the display number as a newline-
  96. * terminated string; copy this number to form a proper display string. */
  97. fseek(display_fd, 0, SEEK_SET);
  98. length = fread(&display[1], 1, sizeof(display) - 2, display_fd);
  99. if (length <= 0) {
  100. fprintf(stderr, "fread error: length=%zu\n", length);
  101. ret = TEST_SETUP_FAILURE;
  102. goto err_xvfd;
  103. } else {
  104. /* Drop the newline character */
  105. display[length] = '\0';
  106. }
  107. /* Run the function requiring a running X server.
  108. * Because it may call abort() via assert(), we fork to be able to
  109. * exit gracefully and not hang waiting for Xvfb. */
  110. pid_t test_pid = fork();
  111. switch (test_pid) {
  112. case -1:
  113. perror("fork");
  114. ret = TEST_SETUP_FAILURE;
  115. break;
  116. case 0:
  117. fprintf(stderr, "Running test using Xvfb wrapper...\n");
  118. ret = test_func(display, private);
  119. fprintf(stderr,
  120. "Test using Xvfb wrapper finished with code %d.\n", ret);
  121. _exit(ret);
  122. default:
  123. {
  124. int test_status = 0;
  125. pid_t test_pid2 = waitpid(test_pid, &test_status, 0);
  126. ret = (test_pid2 > 0 && WIFEXITED(test_status))
  127. ? WEXITSTATUS(test_status)
  128. : EXIT_FAILURE;
  129. fprintf(stderr,
  130. "Test finished with code %d. "
  131. "Shutting down Xvfb (pid: %d)...\n",
  132. ret, xvfb_pid);
  133. }
  134. }
  135. err_xvfd:
  136. if (xvfb_pid > 0) {
  137. fprintf(stderr, "Sending SIGTERM to Xvfb...\n");
  138. kill(xvfb_pid, SIGTERM);
  139. fprintf(stderr, "Waiting for Xvfb to exit (pid: %d)...\n", xvfb_pid);
  140. int xvfb_status = 0;
  141. if (waitpid(xvfb_pid, &xvfb_status, 0) <= 0) {
  142. perror("Xvfb waitpid failed.");
  143. } else if (WIFEXITED(xvfb_status)) {
  144. fprintf(stderr, "Xvfb shut down (pid: %d) with exit code %d.\n",
  145. xvfb_pid, WEXITSTATUS(xvfb_status));
  146. } else {
  147. fprintf(stderr, "Xvfb shut down (pid: %d) abnormally.\n", xvfb_pid);
  148. }
  149. }
  150. fclose(display_fd);
  151. err_display_fd:
  152. return ret;
  153. }
  154. /* All X11_TEST functions are in the test_func_sec ELF section.
  155. * __start and __stop point to the start and end of that section. See the
  156. * __attribute__(section) documentation.
  157. */
  158. DECLARE_TEST_ELF_SECTION_POINTERS(TEST_ELF_SECTION);
  159. int
  160. x11_tests_run(void)
  161. {
  162. int rc = 0;
  163. for (const struct test_function *t = __start_test_func_sec;
  164. t < __stop_test_func_sec;
  165. t++) {
  166. fprintf(stderr, "------ Running test: %s from %s ------\n",
  167. t->name, t->file);
  168. rc = xvfb_wrapper(t->func, NULL);
  169. if (rc != EXIT_SUCCESS) {
  170. break;
  171. }
  172. }
  173. return rc;
  174. }