1 #define _POSIX_C_SOURCE 200809L
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdlib.h>
8 #include <stdnoreturn.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <wayland-server-core.h>
15 #include <wlr/util/log.h>
16 #include <wlr/xwayland.h>
17 #include "sockets.h"
18 #include "util/signal.h"
19
safe_close(int fd)20 static void safe_close(int fd) {
21 if (fd >= 0) {
22 close(fd);
23 }
24 }
25
fill_arg(char *** argv,const char * fmt,...)26 static int fill_arg(char ***argv, const char *fmt, ...) {
27 int len;
28 char **cur_arg = *argv;
29 va_list args;
30 va_start(args, fmt);
31 len = vsnprintf(NULL, 0, fmt, args) + 1;
32 va_end(args);
33 while (*cur_arg) {
34 cur_arg++;
35 }
36 *cur_arg = malloc(len);
37 if (!*cur_arg) {
38 return -1;
39 }
40 *argv = cur_arg;
41 va_start(args, fmt);
42 len = vsnprintf(*cur_arg, len, fmt, args);
43 va_end(args);
44 return len;
45 }
46
exec_xwayland(struct wlr_xwayland_server * server)47 noreturn static void exec_xwayland(struct wlr_xwayland_server *server) {
48 if (!set_cloexec(server->x_fd[0], false) ||
49 !set_cloexec(server->x_fd[1], false) ||
50 !set_cloexec(server->wl_fd[1], false)) {
51 wlr_log(WLR_ERROR, "Failed to unset CLOEXEC on FD");
52 _exit(EXIT_FAILURE);
53 }
54 if (server->enable_wm && !set_cloexec(server->wm_fd[1], false)) {
55 wlr_log(WLR_ERROR, "Failed to unset CLOEXEC on FD");
56 _exit(EXIT_FAILURE);
57 }
58
59 /* Make Xwayland signal us when it's ready */
60 signal(SIGUSR1, SIG_IGN);
61
62 char *argv[] = {
63 "Xwayland", NULL /* display, e.g. :1 */,
64 "-rootless", "-terminate",
65 "-listen", NULL /* x_fd[0] */,
66 "-listen", NULL /* x_fd[1] */,
67 "-wm", NULL /* wm_fd[1] */,
68 NULL,
69 };
70 char **cur_arg = argv;
71
72 if (fill_arg(&cur_arg, ":%d", server->display) < 0 ||
73 fill_arg(&cur_arg, "%d", server->x_fd[0]) < 0 ||
74 fill_arg(&cur_arg, "%d", server->x_fd[1]) < 0) {
75 wlr_log_errno(WLR_ERROR, "alloc/print failure");
76 _exit(EXIT_FAILURE);
77 }
78 if (server->enable_wm) {
79 if (fill_arg(&cur_arg, "%d", server->wm_fd[1]) < 0) {
80 wlr_log_errno(WLR_ERROR, "alloc/print failure");
81 _exit(EXIT_FAILURE);
82 }
83 } else {
84 cur_arg++;
85 *cur_arg = NULL;
86 }
87
88 char wayland_socket_str[16];
89 snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", server->wl_fd[1]);
90 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
91
92 wlr_log(WLR_INFO, "WAYLAND_SOCKET=%d Xwayland :%d -rootless -terminate -listen %d -listen %d -wm %d",
93 server->wl_fd[1], server->display, server->x_fd[0],
94 server->x_fd[1], server->wm_fd[1]);
95
96 // Closes stdout/stderr depending on log verbosity
97 enum wlr_log_importance verbosity = wlr_log_get_verbosity();
98 int devnull = open("/dev/null", O_WRONLY | O_CREAT | O_CLOEXEC, 0666);
99 if (devnull < 0) {
100 wlr_log_errno(WLR_ERROR, "XWayland: failed to open /dev/null");
101 _exit(EXIT_FAILURE);
102 }
103 if (verbosity < WLR_INFO) {
104 dup2(devnull, STDOUT_FILENO);
105 }
106 if (verbosity < WLR_ERROR) {
107 dup2(devnull, STDERR_FILENO);
108 }
109
110 const char *xwayland_path = getenv("WLR_XWAYLAND");
111 if (xwayland_path) {
112 wlr_log(WLR_INFO, "Using Xwayland binary '%s' due to WLR_XWAYLAND",
113 xwayland_path);
114 } else {
115 xwayland_path = "Xwayland";
116 }
117
118 // This returns if and only if the call fails
119 execvp(xwayland_path, argv);
120
121 wlr_log_errno(WLR_ERROR, "failed to exec Xwayland");
122 close(devnull);
123 _exit(EXIT_FAILURE);
124 }
125
server_finish_process(struct wlr_xwayland_server * server)126 static void server_finish_process(struct wlr_xwayland_server *server) {
127 if (!server || server->display == -1) {
128 return;
129 }
130
131 if (server->x_fd_read_event[0]) {
132 wl_event_source_remove(server->x_fd_read_event[0]);
133 wl_event_source_remove(server->x_fd_read_event[1]);
134
135 server->x_fd_read_event[0] = server->x_fd_read_event[1] = NULL;
136 }
137
138 if (server->client) {
139 wl_list_remove(&server->client_destroy.link);
140 wl_client_destroy(server->client);
141 }
142 if (server->sigusr1_source) {
143 wl_event_source_remove(server->sigusr1_source);
144 }
145
146 safe_close(server->wl_fd[0]);
147 safe_close(server->wl_fd[1]);
148 safe_close(server->wm_fd[0]);
149 safe_close(server->wm_fd[1]);
150 memset(server, 0, offsetof(struct wlr_xwayland_server, display));
151 server->wl_fd[0] = server->wl_fd[1] = -1;
152 server->wm_fd[0] = server->wm_fd[1] = -1;
153
154 /* We do not kill the Xwayland process, it dies to broken pipe
155 * after we close our side of the wm/wl fds. This is more reliable
156 * than trying to kill something that might no longer be Xwayland.
157 */
158 }
159
server_finish_display(struct wlr_xwayland_server * server)160 static void server_finish_display(struct wlr_xwayland_server *server) {
161 if (!server) {
162 return;
163 }
164
165 wl_list_remove(&server->display_destroy.link);
166
167 if (server->display == -1) {
168 return;
169 }
170
171 safe_close(server->x_fd[0]);
172 safe_close(server->x_fd[1]);
173 server->x_fd[0] = server->x_fd[1] = -1;
174
175 unlink_display_sockets(server->display);
176 server->display = -1;
177 server->display_name[0] = '\0';
178 }
179
180 static bool server_start(struct wlr_xwayland_server *server);
181 static bool server_start_lazy(struct wlr_xwayland_server *server);
182
handle_client_destroy(struct wl_listener * listener,void * data)183 static void handle_client_destroy(struct wl_listener *listener, void *data) {
184 struct wlr_xwayland_server *server =
185 wl_container_of(listener, server, client_destroy);
186
187 if (server->sigusr1_source) {
188 // Xwayland failed to start, let the sigusr1 handler deal with it
189 return;
190 }
191
192 // Don't call client destroy: it's being destroyed already
193 server->client = NULL;
194 wl_list_remove(&server->client_destroy.link);
195
196 server_finish_process(server);
197
198 if (time(NULL) - server->server_start > 5) {
199 if (server->lazy) {
200 wlr_log(WLR_INFO, "Restarting Xwayland (lazy)");
201 server_start_lazy(server);
202 } else {
203 wlr_log(WLR_INFO, "Restarting Xwayland");
204 server_start(server);
205 }
206 }
207 }
208
handle_display_destroy(struct wl_listener * listener,void * data)209 static void handle_display_destroy(struct wl_listener *listener, void *data) {
210 struct wlr_xwayland_server *server =
211 wl_container_of(listener, server, display_destroy);
212
213 // Don't call client destroy: the display is being destroyed, it's too late
214 if (server->client) {
215 server->client = NULL;
216 wl_list_remove(&server->client_destroy.link);
217 }
218
219 wlr_xwayland_server_destroy(server);
220 }
221
xserver_handle_ready(int signal_number,void * data)222 static int xserver_handle_ready(int signal_number, void *data) {
223 struct wlr_xwayland_server *server = data;
224
225 int stat_val = -1;
226 while (waitpid(server->pid, &stat_val, 0) < 0) {
227 if (errno == EINTR) {
228 continue;
229 }
230 wlr_log_errno(WLR_ERROR, "waitpid for Xwayland fork failed");
231 goto error;
232 }
233 if (stat_val) {
234 wlr_log(WLR_ERROR, "Xwayland startup failed, not setting up xwm");
235 goto error;
236 }
237 wlr_log(WLR_DEBUG, "Xserver is ready");
238
239 wl_event_source_remove(server->sigusr1_source);
240 server->sigusr1_source = NULL;
241
242 struct wlr_xwayland_server_ready_event event = {
243 .server = server,
244 .wm_fd = server->wm_fd[0],
245 };
246 wlr_signal_emit_safe(&server->events.ready, &event);
247
248 return 1; /* wayland event loop dispatcher's count */
249
250 error:
251 /* clean up */
252 server_finish_process(server);
253 server_finish_display(server);
254 return 1;
255 }
256
server_start_display(struct wlr_xwayland_server * server,struct wl_display * wl_display)257 static bool server_start_display(struct wlr_xwayland_server *server,
258 struct wl_display *wl_display) {
259 server->display_destroy.notify = handle_display_destroy;
260 wl_display_add_destroy_listener(wl_display, &server->display_destroy);
261
262 server->display = open_display_sockets(server->x_fd);
263 if (server->display < 0) {
264 server_finish_display(server);
265 return false;
266 }
267
268 snprintf(server->display_name, sizeof(server->display_name),
269 ":%d", server->display);
270 return true;
271 }
272
server_start(struct wlr_xwayland_server * server)273 static bool server_start(struct wlr_xwayland_server *server) {
274 if (socketpair(AF_UNIX, SOCK_STREAM, 0, server->wl_fd) != 0) {
275 wlr_log_errno(WLR_ERROR, "socketpair failed");
276 server_finish_process(server);
277 return false;
278 }
279 if (!set_cloexec(server->wl_fd[0], true) ||
280 !set_cloexec(server->wl_fd[1], true)) {
281 wlr_log(WLR_ERROR, "Failed to set O_CLOEXEC on socket");
282 server_finish_process(server);
283 return false;
284 }
285 if (server->enable_wm) {
286 if (socketpair(AF_UNIX, SOCK_STREAM, 0, server->wm_fd) != 0) {
287 wlr_log_errno(WLR_ERROR, "socketpair failed");
288 server_finish_process(server);
289 return false;
290 }
291 if (!set_cloexec(server->wm_fd[0], true) ||
292 !set_cloexec(server->wm_fd[1], true)) {
293 wlr_log(WLR_ERROR, "Failed to set O_CLOEXEC on socket");
294 server_finish_process(server);
295 return false;
296 }
297 }
298
299 server->server_start = time(NULL);
300
301 server->client = wl_client_create(server->wl_display, server->wl_fd[0]);
302 if (!server->client) {
303 wlr_log_errno(WLR_ERROR, "wl_client_create failed");
304 server_finish_process(server);
305 return false;
306 }
307
308 server->wl_fd[0] = -1; /* not ours anymore */
309
310 server->client_destroy.notify = handle_client_destroy;
311 wl_client_add_destroy_listener(server->client, &server->client_destroy);
312
313 struct wl_event_loop *loop = wl_display_get_event_loop(server->wl_display);
314 server->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
315 xserver_handle_ready, server);
316
317 server->pid = fork();
318 if (server->pid < 0) {
319 wlr_log_errno(WLR_ERROR, "fork failed");
320 server_finish_process(server);
321 return false;
322 } else if (server->pid == 0) {
323 /* Double-fork, but we need to forward SIGUSR1 once Xserver(1)
324 * is ready, or error if there was one. */
325 pid_t ppid = getppid();
326 sigset_t sigset;
327 sigemptyset(&sigset);
328 sigaddset(&sigset, SIGUSR1);
329 sigaddset(&sigset, SIGCHLD);
330 sigprocmask(SIG_BLOCK, &sigset, NULL);
331
332 pid_t pid = fork();
333 if (pid < 0) {
334 wlr_log_errno(WLR_ERROR, "second fork failed");
335 _exit(EXIT_FAILURE);
336 } else if (pid == 0) {
337 exec_xwayland(server);
338 }
339
340 int sig;
341 sigwait(&sigset, &sig);
342 kill(ppid, SIGUSR1);
343 wlr_log(WLR_DEBUG, "sent SIGUSR1 to process %d", ppid);
344 if (sig == SIGCHLD) {
345 waitpid(pid, NULL, 0);
346 _exit(EXIT_FAILURE);
347 }
348
349 _exit(EXIT_SUCCESS);
350 }
351
352 /* close child fds */
353 /* remain managing x sockets for lazy start */
354 close(server->wl_fd[1]);
355 safe_close(server->wm_fd[1]);
356 server->wl_fd[1] = server->wm_fd[1] = -1;
357
358 return true;
359 }
360
xwayland_socket_connected(int fd,uint32_t mask,void * data)361 static int xwayland_socket_connected(int fd, uint32_t mask, void *data) {
362 struct wlr_xwayland_server *server = data;
363
364 wl_event_source_remove(server->x_fd_read_event[0]);
365 wl_event_source_remove(server->x_fd_read_event[1]);
366 server->x_fd_read_event[0] = server->x_fd_read_event[1] = NULL;
367
368 server_start(server);
369
370 return 0;
371 }
372
server_start_lazy(struct wlr_xwayland_server * server)373 static bool server_start_lazy(struct wlr_xwayland_server *server) {
374 struct wl_event_loop *loop = wl_display_get_event_loop(server->wl_display);
375
376 if (!(server->x_fd_read_event[0] = wl_event_loop_add_fd(loop, server->x_fd[0],
377 WL_EVENT_READABLE, xwayland_socket_connected, server))) {
378 return false;
379 }
380
381 if (!(server->x_fd_read_event[1] = wl_event_loop_add_fd(loop, server->x_fd[1],
382 WL_EVENT_READABLE, xwayland_socket_connected, server))) {
383 wl_event_source_remove(server->x_fd_read_event[0]);
384 server->x_fd_read_event[0] = NULL;
385 return false;
386 }
387
388 return true;
389 }
390
wlr_xwayland_server_destroy(struct wlr_xwayland_server * server)391 void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server) {
392 if (!server) {
393 return;
394 }
395
396 server_finish_process(server);
397 server_finish_display(server);
398 wlr_signal_emit_safe(&server->events.destroy, NULL);
399 free(server);
400 }
401
wlr_xwayland_server_create(struct wl_display * wl_display,struct wlr_xwayland_server_options * options)402 struct wlr_xwayland_server *wlr_xwayland_server_create(
403 struct wl_display *wl_display,
404 struct wlr_xwayland_server_options *options) {
405 struct wlr_xwayland_server *server =
406 calloc(1, sizeof(struct wlr_xwayland_server));
407 if (!server) {
408 return NULL;
409 }
410
411 server->wl_display = wl_display;
412 server->lazy = options->lazy;
413 server->enable_wm = options->enable_wm;
414
415 server->x_fd[0] = server->x_fd[1] = -1;
416 server->wl_fd[0] = server->wl_fd[1] = -1;
417 server->wm_fd[0] = server->wm_fd[1] = -1;
418
419 server->scale = 1;
420
421 wl_signal_init(&server->events.ready);
422 wl_signal_init(&server->events.destroy);
423
424 if (!server_start_display(server, wl_display)) {
425 goto error_alloc;
426 }
427
428 if (server->lazy) {
429 if (!server_start_lazy(server)) {
430 goto error_display;
431 }
432 } else {
433 if (!server_start(server)) {
434 goto error_display;
435 }
436 }
437
438 return server;
439
440 error_display:
441 server_finish_display(server);
442 error_alloc:
443 free(server);
444 return NULL;
445 }
446