parent
							
								
									a138657598
								
							
						
					
					
						commit
						49a823d4c6
					
				| @ -0,0 +1,17 @@ | |||||||
|  | #ifndef _WLR_XWAYLAND_H | ||||||
|  | #define _WLR_XWAYLAND_H | ||||||
|  | 
 | ||||||
|  | struct wlr_xwayland { | ||||||
|  |         pid_t pid; | ||||||
|  |         int display; | ||||||
|  |         int x_fd[2], wl_fd[2], wm_fd[2]; | ||||||
|  |         struct wl_client *client; | ||||||
|  | 	struct wl_display *wl_display; | ||||||
|  | 	time_t server_start; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void wlr_xwayland_finish(struct wlr_xwayland *wlr_xwayland); | ||||||
|  | bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland, | ||||||
|  | 		struct wl_display *wl_display); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,6 @@ | |||||||
|  | #ifndef XWAYLAND_INTERNALS_H | ||||||
|  | #define XWAYLAND_INTERNALS_H | ||||||
|  | 
 | ||||||
|  | void unlink_sockets(int display); | ||||||
|  | int open_display_sockets(int socks[2]); | ||||||
|  | #endif | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | lib_wlr_xwayland = static_library('wlr_xwayland', files( | ||||||
|  |     'sockets.c', | ||||||
|  |     'xwayland.c', | ||||||
|  |   ), | ||||||
|  |   include_directories: wlr_inc) | ||||||
| @ -0,0 +1,144 @@ | |||||||
|  | #define _XOPEN_SOURCE 700 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/un.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include "wlr/util/log.h" | ||||||
|  | #include "xwayland/internals.h" | ||||||
|  | 
 | ||||||
|  | static const char *lock_fmt = "/tmp/.X%d-lock"; | ||||||
|  | static const char *socket_dir = "/tmp/.X11-unix"; | ||||||
|  | static const char *socket_fmt = "/tmp/.X11-unix/X%d"; | ||||||
|  | 
 | ||||||
|  | static int open_socket(struct sockaddr_un *addr, size_t path_size) { | ||||||
|  | 	int fd, rc; | ||||||
|  | 	socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1; | ||||||
|  | 
 | ||||||
|  | 	fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "Failed to create socket %s", addr->sun_path); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (addr->sun_path[0]) { | ||||||
|  | 		unlink(addr->sun_path); | ||||||
|  | 	} | ||||||
|  | 	if (bind(fd, (struct sockaddr*)addr, size) < 0) { | ||||||
|  | 		rc = errno; | ||||||
|  | 		wlr_log_errno(L_ERROR, "Failed to bind socket %s", addr->sun_path); | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 	if (listen(fd, 1) < 0) { | ||||||
|  | 		rc = errno; | ||||||
|  | 		wlr_log_errno(L_ERROR, "Failed to listen to socket %s", addr->sun_path); | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return fd; | ||||||
|  | 
 | ||||||
|  | cleanup: | ||||||
|  | 	close(fd); | ||||||
|  | 	if (addr->sun_path[0]) { | ||||||
|  | 		unlink(addr->sun_path); | ||||||
|  | 	} | ||||||
|  | 	errno = rc; | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool open_sockets(int socks[2], int display) { | ||||||
|  | 	struct sockaddr_un addr = { .sun_family = AF_LOCAL }; | ||||||
|  | 	size_t path_size; | ||||||
|  | 
 | ||||||
|  | 	mkdir(socket_dir, 0777); | ||||||
|  | 
 | ||||||
|  | 	// TODO: non-linux apparently want another format
 | ||||||
|  | 	addr.sun_path[0] = 0; | ||||||
|  | 	path_size = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, socket_fmt, display); | ||||||
|  | 	socks[0] = open_socket(&addr, path_size); | ||||||
|  | 	if (socks[0] < 0) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), socket_fmt, display); | ||||||
|  | 	socks[1] = open_socket(&addr, path_size); | ||||||
|  | 	if (socks[1] < 0) { | ||||||
|  | 		close(socks[0]); | ||||||
|  | 		socks[0] = -1; | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unlink_sockets(int display) { | ||||||
|  | 	char sun_path[64]; | ||||||
|  | 
 | ||||||
|  | 	snprintf(sun_path, sizeof(sun_path), socket_fmt, display); | ||||||
|  | 	unlink(sun_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int open_display_sockets(int socks[2]) { | ||||||
|  | 	int lock_fd, display; | ||||||
|  | 	char lock_name[64]; | ||||||
|  | 
 | ||||||
|  | 	for (display = 0; display <= 32; display++) { | ||||||
|  | 		snprintf(lock_name, sizeof(lock_name), lock_fmt, display); | ||||||
|  | 		if ((lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444)) >= 0) { | ||||||
|  | 			if (!open_sockets(socks, display)) { | ||||||
|  | 				unlink(lock_name); | ||||||
|  | 				close(lock_fd); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			char pid[12]; | ||||||
|  | 			snprintf(pid, sizeof(pid), "%10d", getpid()); | ||||||
|  | 			if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) { | ||||||
|  | 				unlink(lock_name); | ||||||
|  | 				close(lock_fd); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			close(lock_fd); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if ((lock_fd = open(lock_name, O_RDONLY | O_CLOEXEC)) < 0) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		char pid[12] = { 0 }, *end_pid; | ||||||
|  | 		ssize_t bytes = read(lock_fd, pid, sizeof(pid) - 1); | ||||||
|  | 		close(lock_fd); | ||||||
|  | 
 | ||||||
|  | 		if (bytes != sizeof(pid) - 1) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		long int read_pid; | ||||||
|  | 		read_pid = strtol(pid, &end_pid, 10); | ||||||
|  | 		if (read_pid < 0 || read_pid > INT32_MAX || end_pid != pid + sizeof(pid)) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		errno = 0; | ||||||
|  | 		if (kill((pid_t)read_pid, 0) != 0 && errno == ESRCH) { | ||||||
|  | 			if (unlink(lock_name) != 0) { | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			// retry
 | ||||||
|  | 			display--; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (display > 32) { | ||||||
|  | 		wlr_log(L_ERROR, "No display available in the first 33"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return display; | ||||||
|  | } | ||||||
| @ -0,0 +1,170 @@ | |||||||
|  | #define _XOPEN_SOURCE 700 | ||||||
|  | #define _GNU_SOURCE | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <wayland-server.h> | ||||||
|  | #include "wlr/util/log.h" | ||||||
|  | #include "wlr/xwayland.h" | ||||||
|  | #include "xwayland/internals.h" | ||||||
|  | 
 | ||||||
|  | static void safe_close(int fd) { | ||||||
|  | 	if (fd >= 0) { | ||||||
|  | 		close(fd); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int unset_cloexec(int fd) { | ||||||
|  | 	if (fcntl(fd, F_SETFD, 0) != 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "fcntl() failed on fd %d", fd); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) { | ||||||
|  | 	if (unset_cloexec(wlr_xwayland->x_fd[0]) || | ||||||
|  | 			unset_cloexec(wlr_xwayland->x_fd[1]) || | ||||||
|  | 			unset_cloexec(wlr_xwayland->wm_fd[1]) || | ||||||
|  | 			unset_cloexec(wlr_xwayland->wl_fd[1])) { | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	char *argv[11] = { 0 }; | ||||||
|  | 	argv[0] = "Xwayland"; | ||||||
|  | 	if (asprintf(&argv[1], ":%d", wlr_xwayland->display) < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "asprintf failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 	argv[2] = "-rootless"; | ||||||
|  | 	argv[3] = "-terminate"; | ||||||
|  | 	argv[4] = "-listen"; | ||||||
|  | 	if (asprintf(&argv[5], "%d", wlr_xwayland->x_fd[0]) < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "asprintf failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 	argv[6] = "-listen"; | ||||||
|  | 	if (asprintf(&argv[7], "%d", wlr_xwayland->x_fd[1]) < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "asprintf failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 	argv[8] = "-wm"; | ||||||
|  | 	if (asprintf(&argv[9], "%d", wlr_xwayland->wm_fd[1]) < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "asprintf failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const char *xdg_runtime = getenv("XDG_RUNTIME_DIR"); | ||||||
|  | 	if (!xdg_runtime) { | ||||||
|  | 		wlr_log(L_ERROR, "XDG_RUNTIME_DIR is not set"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	char *envp[3] = { 0 }; | ||||||
|  | 	if (asprintf(&envp[0], "XDG_RUNTIME_DIR=%s", xdg_runtime) < 0 || | ||||||
|  | 			asprintf(&envp[1], "WAYLAND_SOCKET=%d", wlr_xwayland->wl_fd[1]) < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "asprintf failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wlr_log(L_INFO, "Xwayland :%d -rootless -terminate -listen %d -listen %d -wm %d", | ||||||
|  | 			wlr_xwayland->display, wlr_xwayland->x_fd[0], wlr_xwayland->x_fd[1], | ||||||
|  | 			wlr_xwayland->wm_fd[1]); | ||||||
|  | 
 | ||||||
|  | 	execvpe("Xwayland", argv, envp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xwayland_destroy_event(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct wl_client *client = data; | ||||||
|  | 	struct wlr_xwayland *wlr_xwayland = wl_container_of(client, wlr_xwayland, client); | ||||||
|  | 
 | ||||||
|  | 	/* don't call client destroy */ | ||||||
|  | 	wlr_xwayland->client = NULL; | ||||||
|  | 	wlr_xwayland_finish(wlr_xwayland); | ||||||
|  | 
 | ||||||
|  | 	if (wlr_xwayland->server_start - time(NULL) > 5) { | ||||||
|  | 		wlr_xwayland_init(wlr_xwayland, wlr_xwayland->wl_display); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wl_listener xwayland_destroy_listener = { | ||||||
|  | 	.notify = xwayland_destroy_event, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void wlr_xwayland_finish(struct wlr_xwayland *wlr_xwayland) { | ||||||
|  | 
 | ||||||
|  | 	if (wlr_xwayland->client) { | ||||||
|  | 		wl_list_remove(&xwayland_destroy_listener.link); | ||||||
|  | 		wl_client_destroy(wlr_xwayland->client); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	safe_close(wlr_xwayland->x_fd[0]); | ||||||
|  | 	safe_close(wlr_xwayland->x_fd[1]); | ||||||
|  | 	safe_close(wlr_xwayland->wl_fd[0]); | ||||||
|  | 	safe_close(wlr_xwayland->wl_fd[1]); | ||||||
|  | 	safe_close(wlr_xwayland->wm_fd[0]); | ||||||
|  | 	safe_close(wlr_xwayland->wm_fd[1]); | ||||||
|  | 
 | ||||||
|  | 	unlink_sockets(wlr_xwayland->display);	 | ||||||
|  | 	unsetenv("DISPLAY"); | ||||||
|  | 	/* kill Xwayland process? */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool wlr_xwayland_init(struct wlr_xwayland *wlr_xwayland, | ||||||
|  | 		struct wl_display *wl_display) { | ||||||
|  | 	wlr_xwayland->wl_display = wl_display; | ||||||
|  | 	wlr_xwayland->x_fd[0] = wlr_xwayland->x_fd[1] = -1; | ||||||
|  | 	wlr_xwayland->wl_fd[0] = wlr_xwayland->wl_fd[1] = -1; | ||||||
|  | 	wlr_xwayland->wm_fd[0] = wlr_xwayland->wm_fd[1] = -1; | ||||||
|  | 
 | ||||||
|  | 	wlr_xwayland->display = open_display_sockets(wlr_xwayland->x_fd); | ||||||
|  | 	if (wlr_xwayland->display < 0) { | ||||||
|  | 		wlr_xwayland_finish(wlr_xwayland); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wlr_xwayland->wl_fd) != 0 || | ||||||
|  | 			socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wlr_xwayland->wm_fd) != 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "failed to create socketpair"); | ||||||
|  | 		wlr_xwayland_finish(wlr_xwayland); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ((wlr_xwayland->pid = fork()) == 0) { | ||||||
|  | 		exec_xwayland(wlr_xwayland); | ||||||
|  | 		wlr_log_errno(L_ERROR, "execvpe failed"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (wlr_xwayland->pid < 0) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "fork failed"); | ||||||
|  | 		wlr_xwayland_finish(wlr_xwayland); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* close child fds */ | ||||||
|  | 	close(wlr_xwayland->x_fd[0]); | ||||||
|  | 	close(wlr_xwayland->x_fd[1]); | ||||||
|  | 	close(wlr_xwayland->wl_fd[1]); | ||||||
|  | 	close(wlr_xwayland->wm_fd[1]); | ||||||
|  | 	wlr_xwayland->x_fd[0] = wlr_xwayland->x_fd[1] = -1; | ||||||
|  | 	wlr_xwayland->wl_fd[1] = wlr_xwayland->wm_fd[1] = -1; | ||||||
|  | 
 | ||||||
|  | 	char display_name[16]; | ||||||
|  | 	snprintf(display_name, sizeof(display_name), ":%d", wlr_xwayland->display); | ||||||
|  | 	setenv("DISPLAY", display_name, true); | ||||||
|  | 	wlr_xwayland->server_start = time(NULL); | ||||||
|  | 
 | ||||||
|  | 	if (!(wlr_xwayland->client = wl_client_create(wl_display, wlr_xwayland->wl_fd[0]))) { | ||||||
|  | 		wlr_log_errno(L_ERROR, "wl_client_create failed"); | ||||||
|  | 		wlr_xwayland_finish(wlr_xwayland); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wl_client_add_destroy_listener(wlr_xwayland->client, &xwayland_destroy_listener); | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue