Xwayland: first draft, just start server for now
This commit is contained in:
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
|
|
@ -50,6 +50,7 @@ subdir('render')
|
||||||
subdir('types')
|
subdir('types')
|
||||||
subdir('util')
|
subdir('util')
|
||||||
subdir('xcursor')
|
subdir('xcursor')
|
||||||
|
subdir('xwayland')
|
||||||
|
|
||||||
wlr_deps = [
|
wlr_deps = [
|
||||||
wayland_server,
|
wayland_server,
|
||||||
|
@ -77,6 +78,7 @@ lib_wlr = library('wlroots', files('dummy.c'),
|
||||||
lib_wlr_types,
|
lib_wlr_types,
|
||||||
lib_wlr_util,
|
lib_wlr_util,
|
||||||
lib_wlr_xcursor,
|
lib_wlr_xcursor,
|
||||||
|
lib_wlr_xwayland,
|
||||||
],
|
],
|
||||||
dependencies: wlr_deps,
|
dependencies: wlr_deps,
|
||||||
include_directories: wlr_inc)
|
include_directories: wlr_inc)
|
||||||
|
|
|
@ -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