diff options
Diffstat (limited to 'src/wayland/meta-xwayland.c')
-rw-r--r-- | src/wayland/meta-xwayland.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c new file mode 100644 index 000000000..7ff30bf92 --- /dev/null +++ b/src/wayland/meta-xwayland.c @@ -0,0 +1,310 @@ +/* + * X Wayland Support + * + * Copyright (C) 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "meta-xwayland-private.h" + +#include <glib.h> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/un.h> + +static char * +create_lockfile (int display, int *display_out) +{ + char *filename; + int size; + char pid[11]; + int fd; + + do + { + char *end; + pid_t other; + + filename = g_strdup_printf ("/tmp/.X%d-lock", display); + fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444); + + if (fd < 0 && errno == EEXIST) + { + fd = open (filename, O_CLOEXEC, O_RDONLY); + if (fd < 0 || read (fd, pid, 11) != 11) + { + const char *msg = strerror (errno); + g_warning ("can't read lock file %s: %s", filename, msg); + g_free (filename); + + /* ignore error and try the next display number */ + display++; + continue; + } + close (fd); + + other = strtol (pid, &end, 0); + if (end != pid + 10) + { + g_warning ("can't parse lock file %s", filename); + g_free (filename); + + /* ignore error and try the next display number */ + display++; + continue; + } + + if (kill (other, 0) < 0 && errno == ESRCH) + { + g_warning ("unlinking stale lock file %s", filename); + if (unlink (filename) < 0) + { + const char *msg = strerror (errno); + g_warning ("failed to unlink stale lock file: %s", msg); + display++; + } + g_free (filename); + continue; + } + + g_free (filename); + display++; + continue; + } + else if (fd < 0) + { + const char *msg = strerror (errno); + g_warning ("failed to create lock file %s: %s", filename , msg); + g_free (filename); + return NULL; + } + + break; + } + while (1); + + /* Subtle detail: we use the pid of the wayland compositor, not the xserver + * in the lock file. */ + size = snprintf (pid, 11, "%10d\n", getpid ()); + if (size != 11 || write (fd, pid, 11) != 11) + { + unlink (filename); + close (fd); + g_warning ("failed to write pid to lock file %s", filename); + g_free (filename); + return NULL; + } + + close (fd); + + *display_out = display; + return filename; +} + +static int +bind_to_abstract_socket (int display) +{ + struct sockaddr_un addr; + socklen_t size, name_size; + int fd; + + fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + addr.sun_family = AF_LOCAL; + name_size = snprintf (addr.sun_path, sizeof addr.sun_path, + "%c/tmp/.X11-unix/X%d", 0, display); + size = offsetof (struct sockaddr_un, sun_path) + name_size; + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { + g_warning ("failed to bind to @%s: %s\n", + addr.sun_path + 1, strerror (errno)); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) + { + close (fd); + return -1; + } + + return fd; +} + +static int +bind_to_unix_socket (int display) +{ + struct sockaddr_un addr; + socklen_t size, name_size; + int fd; + + fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + addr.sun_family = AF_LOCAL; + name_size = snprintf (addr.sun_path, sizeof addr.sun_path, + "/tmp/.X11-unix/X%d", display) + 1; + size = offsetof (struct sockaddr_un, sun_path) + name_size; + unlink (addr.sun_path); + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { + char *msg = strerror (errno); + g_warning ("failed to bind to %s (%s)\n", addr.sun_path, msg); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) { + unlink (addr.sun_path); + close (fd); + return -1; + } + + return fd; +} + +gboolean +meta_xwayland_start (MetaWaylandCompositor *compositor) +{ + int display = 0; + char *lockfile = NULL; + int sp[2]; + pid_t pid; + + do + { + lockfile = create_lockfile (display, &display); + if (!lockfile) + { + g_warning ("Failed to create an X lock file"); + return FALSE; + } + + compositor->xwayland_abstract_fd = bind_to_abstract_socket (display); + if (compositor->xwayland_abstract_fd < 0) + { + unlink (lockfile); + + if (errno == EADDRINUSE) + { + display++; + continue; + } + else + return FALSE; + } + + compositor->xwayland_unix_fd = bind_to_unix_socket (display); + if (compositor->xwayland_abstract_fd < 0) + { + unlink (lockfile); + close (compositor->xwayland_abstract_fd); + return FALSE; + } + + break; + } + while (1); + + compositor->xwayland_display_index = display; + compositor->xwayland_lockfile = lockfile; + + /* We want xwayland to be a wayland client so we make a socketpair to setup a + * wayland protocol connection. */ + if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp) < 0) + { + g_warning ("socketpair failed\n"); + unlink (lockfile); + return 1; + } + + switch ((pid = fork())) + { + case 0: + { + char *fd_string; + char *display_name; + /* Make sure the client end of the socket pair doesn't get closed + * when we exec xwayland. */ + int flags = fcntl (sp[1], F_GETFD); + if (flags != -1) + fcntl (sp[1], F_SETFD, flags & ~FD_CLOEXEC); + + fd_string = g_strdup_printf ("%d", sp[1]); + setenv ("WAYLAND_SOCKET", fd_string, 1); + g_free (fd_string); + + display_name = g_strdup_printf (":%d", + compositor->xwayland_display_index); + + if (execl (XWAYLAND_PATH, + XWAYLAND_PATH, + display_name, + "-wayland", + "-rootless", + "-retro", + "-noreset", + /* FIXME: does it make sense to log to the filesystem by + * default? */ + "-logfile", "/tmp/xwayland.log", + "-nolisten", "all", + NULL) < 0) + { + char *msg = strerror (errno); + g_warning ("xwayland exec failed: %s", msg); + } + exit (-1); + return FALSE; + } + default: + g_message ("forked X server, pid %d\n", pid); + + close (sp[1]); + compositor->xwayland_client = + wl_client_create (compositor->wayland_display, sp[0]); + + compositor->xwayland_pid = pid; + break; + + case -1: + g_error ("Failed to fork for xwayland server"); + return FALSE; + } + + return TRUE; +} + +void +meta_xwayland_stop (MetaWaylandCompositor *compositor) +{ + char path[256]; + + snprintf (path, sizeof path, "/tmp/.X%d-lock", + compositor->xwayland_display_index); + unlink (path); + snprintf (path, sizeof path, "/tmp/.X11-unix/X%d", + compositor->xwayland_display_index); + unlink (path); + + unlink (compositor->xwayland_lockfile); +} |