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