[mutter/wip/wayland-display: 49/93] wayland: add TTY and DRM master management through weston-launch



commit cb44173599a66266688dcfeba796352f579bdae7
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Mon Jul 15 18:07:01 2013 +0200

    wayland: add TTY and DRM master management through weston-launch
    
    To run mutter as a display server, one needs to acquire and
    release the DRM master, which is only possible for root, so
    we take advantage of weston-launch, a small setuid helper binary
    written for the weston project. We import our own slightly
    modified copy of it, because weston-launch only launches weston,
    for security reasons.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=705861

 configure.ac                       |    3 +-
 src/Makefile.am                    |   19 +-
 src/wayland/meta-tty.c             |  399 ++++++++++++++++++++
 src/wayland/meta-tty.h             |   43 +++
 src/wayland/meta-wayland-private.h |    5 +
 src/wayland/meta-wayland.c         |   84 +++++-
 src/wayland/meta-weston-launch.c   |  202 +++++++++++
 src/wayland/meta-weston-launch.h   |   56 +++
 src/wayland/weston-launch.c        |  703 ++++++++++++++++++++++++++++++++++++
 src/wayland/weston-launch.h        |   46 +++
 10 files changed, 1557 insertions(+), 3 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 4a4d2da..464d6a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,6 +141,7 @@ PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0)
 # gtk_window_set_icon_name requires gtk2+-2.6.0
 PKG_CHECK_MODULES(MUTTER_MESSAGE, gtk+-3.0)
 PKG_CHECK_MODULES(MUTTER_WINDOW_DEMO, gtk+-3.0)
+PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login)
 
 # Unconditionally use this dir to avoid a circular dep with gnomecc
 GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
@@ -242,7 +243,7 @@ if test x$enable_wayland = "xyes"; then
 
   AC_SUBST(XWAYLAND_PATH)
 
-  MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server clutter-wayland-compositor-1.0"
+  MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server clutter-wayland-compositor-1.0 libdrm"
   AC_DEFINE(HAVE_WAYLAND, , [Building with Wayland support])
   have_wayland=yes
 fi
diff --git a/src/Makefile.am b/src/Makefile.am
index 6fed47b..297df3d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -176,6 +176,8 @@ libmutter_la_SOURCES =                              \
 
 if HAVE_WAYLAND
 libmutter_la_SOURCES +=                                \
+       wayland/meta-tty.c                      \
+       wayland/meta-tty.h                      \
        wayland/meta-wayland.c                  \
        wayland/meta-wayland-private.h          \
        wayland/meta-xwayland-private.h         \
@@ -189,7 +191,9 @@ libmutter_la_SOURCES +=                             \
        wayland/meta-wayland-seat.c             \
        wayland/meta-wayland-seat.h             \
        wayland/meta-wayland-stage.h            \
-       wayland/meta-wayland-stage.c
+       wayland/meta-wayland-stage.c            \
+       wayland/meta-weston-launch.c            \
+       wayland/meta-weston-launch.h
 endif
 
 libmutter_la_LDFLAGS = -no-undefined
@@ -244,6 +248,19 @@ bin_PROGRAMS=mutter mutter-theme-viewer
 mutter_SOURCES = core/mutter.c
 mutter_LDADD = $(MUTTER_LIBS) libmutter.la
 
+if HAVE_WAYLAND
+bin_PROGRAMS+=mutter-launch
+
+mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h
+
+mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS)
+mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam
+
+install-exec-hook:
+       -chown root $(DESTDIR)$(bindir)/mutter-launch
+       -chmod u+s $(DESTDIR)$(bindir)/mutter-launch
+endif
+
 if HAVE_INTROSPECTION
 include $(INTROSPECTION_MAKEFILE)
 
diff --git a/src/wayland/meta-tty.c b/src/wayland/meta-tty.c
new file mode 100644
index 0000000..8aae4c7
--- /dev/null
+++ b/src/wayland/meta-tty.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *             2013 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/major.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "meta-tty.h"
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+/* Introduced in 2.6.38 */
+#ifndef K_OFF
+#define K_OFF 0x04
+#endif
+
+struct _MetaTTYClass
+{
+  GObjectClass parent_class;
+};
+
+struct _MetaTTY
+{
+  GObject parent;
+
+  int fd;
+  struct termios terminal_attributes;
+
+  int input_source, vt_source;
+  int vt, starting_vt;
+  gboolean has_vt;
+  int kb_mode;
+};
+
+enum {
+  SIGNAL_ENTER,
+  SIGNAL_LEAVE,
+  SIGNAL_LAST
+};
+
+static int signals[SIGNAL_LAST];
+
+static void meta_tty_initable_iface_init (GInitableIface *);
+
+G_DEFINE_TYPE_WITH_CODE (MetaTTY, meta_tty, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               meta_tty_initable_iface_init));
+
+static int
+vt_handler (gpointer user_data)
+{
+  MetaTTY *tty = user_data;
+
+  if (tty->has_vt)
+    {
+      tty->has_vt = FALSE;
+      g_signal_emit (tty, signals[SIGNAL_LEAVE], 0);
+
+      ioctl (tty->fd, VT_RELDISP, 1);
+    }
+  else
+    {
+      ioctl (tty->fd, VT_RELDISP, VT_ACKACQ);
+
+      tty->has_vt = 1;
+      g_signal_emit (tty, signals[SIGNAL_ENTER], 0);
+    }
+
+  return 1;
+}
+
+static int
+on_tty_input (int          fd, 
+             GIOCondition mask,
+             gpointer     user_data)
+{
+  MetaTTY *tty = user_data;
+
+  /* Ignore input to tty.  We get keyboard events from evdev */
+  tcflush(tty->fd, TCIFLUSH);
+
+  return 1;
+}
+
+static int
+try_open_vt (MetaTTY  *tty,
+            GError  **error)
+{
+  int tty0, fd;
+  char filename[16];
+
+  tty0 = open ("/dev/tty0", O_WRONLY | O_CLOEXEC);
+  if (tty0 < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      return -1;
+    }
+
+  if (ioctl (tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      close (tty0);
+      return -1;
+  }
+
+  close (tty0);
+  snprintf (filename, sizeof filename, "/dev/tty%d", tty->vt);
+  g_debug("compositor: using new vt %s\n", filename);
+  fd = open (filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
+  return fd;
+}
+
+/* FIXME? */
+static int
+tty_activate_vt (MetaTTY *tty,
+                int      vt)
+{
+  return ioctl(tty->fd, VT_ACTIVATE, vt);
+}
+
+static int
+env_get_fd (const char *env)
+{
+  const char *value;
+
+  value = g_getenv (env);
+
+  if (value == NULL)
+    return -1;
+  else
+    return g_ascii_strtoll (env, NULL, 10);
+}
+
+static gboolean
+meta_tty_initable_init(GInitable     *initable,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  MetaTTY *tty = META_TTY (initable);
+  struct termios raw_attributes;
+  struct vt_mode mode = { 0 };
+  int ret;
+       
+  struct stat buf;
+  struct vt_stat vts;
+
+  tty->fd = env_get_fd ("WESTON_TTY_FD");
+  if (tty->fd < 0)
+    tty->fd = STDIN_FILENO;
+
+  if (fstat(tty->fd, &buf) == 0 &&
+      major(buf.st_rdev) == TTY_MAJOR &&
+      minor(buf.st_rdev) > 0)
+    {
+      if (tty->fd == STDIN_FILENO)
+       tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
+      tty->vt = minor(buf.st_rdev);
+    }
+  else
+    {
+      /* Fall back to try opening a new VT.  This typically
+       * requires root. */
+      tty->fd = try_open_vt(tty, error);
+    }
+
+  if (tty->fd <= 0 && (!error || !*error))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      return FALSE;
+    }
+
+  if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
+    tty->starting_vt = vts.v_active;
+  else
+    tty->starting_vt = tty->vt;
+  
+  if (tty->starting_vt != tty->vt)
+    {
+      if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
+         ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      "Failed to switch to new vt: %s", strerror (errno));
+         goto err;
+       }
+    }
+
+  if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not get terminal attributes: %s", strerror (errno));
+      goto err;
+    }
+
+  /* Ignore control characters and disable echo */
+  raw_attributes = tty->terminal_attributes;
+  cfmakeraw(&raw_attributes);
+
+  /* Fix up line endings to be normal (cfmakeraw hoses them) */
+  raw_attributes.c_oflag |= OPOST | OCRNL;
+  /* Don't generate ttou signals */
+  raw_attributes.c_oflag &= ~TOSTOP;
+
+  if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
+    g_warning("Could not put terminal into raw mode: %s", strerror (errno));
+
+  ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
+  ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
+  if (ret)
+    {
+      ret = ioctl(tty->fd, KDSKBMODE, K_RAW);
+      if (ret)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      "Failed to set keyboard mode: %s", strerror (errno));
+         goto err_attr;
+       }
+
+      tty->input_source = g_unix_fd_add (tty->fd,
+                                        G_IO_IN,
+                                        on_tty_input, tty);
+    }
+
+  ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
+  if (ret)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Failed to set KD_GRAPHICS mode: %s", strerror (errno));
+      goto err_kdkbmode;
+    }
+
+  tty->has_vt = 1;
+  mode.mode = VT_PROCESS;
+  mode.relsig = SIGUSR1;
+  mode.acqsig = SIGUSR1;
+  if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Failed to take control of vt handling: %s", strerror (errno));
+      goto err_kdmode;
+    }
+
+  tty->vt_source = g_unix_signal_add (SIGUSR1, vt_handler, tty);
+
+  return TRUE;
+
+ err_kdmode:
+  ioctl (tty->fd, KDSETMODE, KD_TEXT);
+
+ err_kdkbmode:
+  if (tty->input_source)
+    g_source_remove (tty->input_source);
+  ioctl (tty->fd, KDSKBMODE, tty->kb_mode);
+
+ err_attr:
+  tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes);
+  
+ err:
+  close (tty->fd);
+  return FALSE;
+}
+
+static void
+tty_reset (MetaTTY *tty)
+{
+  struct vt_mode mode = { 0 };
+
+  if (ioctl (tty->fd, KDSKBMODE, tty->kb_mode))
+    g_warning ("failed to restore keyboard mode: %s", strerror (errno));
+
+  if (ioctl (tty->fd, KDSETMODE, KD_TEXT))
+    g_warning ("failed to set KD_TEXT mode on tty: %s", strerror (errno));
+
+  if (tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
+    g_warning ("could not restore terminal to canonical mode");
+
+  mode.mode = VT_AUTO;
+  if (ioctl (tty->fd, VT_SETMODE, &mode) < 0)
+    g_warning ("could not reset vt handling\n");
+
+  if (tty->has_vt && tty->vt != tty->starting_vt)
+    {
+      ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
+      ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
+    }
+}
+
+static void
+meta_tty_finalize (GObject *object)
+{
+  MetaTTY *tty = META_TTY (object);
+
+  if (tty->input_source)
+    g_source_remove (tty->input_source);
+
+  g_source_remove (tty->vt_source);
+
+  tty_reset (tty);
+
+  close (tty->fd);
+
+  G_OBJECT_CLASS (meta_tty_parent_class)->finalize (object);
+}
+
+static void
+meta_tty_init (MetaTTY *self)
+{
+}
+
+static void
+meta_tty_class_init (MetaTTYClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_tty_finalize;
+
+  signals[SIGNAL_ENTER] = g_signal_new ("enter",
+                                       G_TYPE_FROM_CLASS (klass),
+                                       G_SIGNAL_RUN_FIRST,
+                                       0, /* class offset */
+                                       NULL, NULL, /* accumulator */
+                                       g_cclosure_marshal_VOID__VOID,
+                                       G_TYPE_NONE, 0);
+
+  signals[SIGNAL_LEAVE] = g_signal_new ("leave",
+                                       G_TYPE_FROM_CLASS (klass),
+                                       G_SIGNAL_RUN_FIRST,
+                                       0, /* class offset */
+                                       NULL, NULL, /* accumulator */
+                                       g_cclosure_marshal_VOID__VOID,
+                                       G_TYPE_NONE, 0);
+}
+
+static void
+meta_tty_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = meta_tty_initable_init;
+}
+
+MetaTTY *
+meta_tty_new (void)
+{
+  GError *error;
+  MetaTTY *tty;
+
+  error = NULL;
+  tty = g_initable_new (META_TYPE_TTY, NULL, &error, NULL);
+
+  if (tty == NULL)
+    {
+      g_warning ("Failed to initalize TTY handling: %s", error->message);
+      g_error_free (error);
+    }
+
+  return tty;
+}
diff --git a/src/wayland/meta-tty.h b/src/wayland/meta-tty.h
new file mode 100644
index 0000000..2c3bf2a
--- /dev/null
+++ b/src/wayland/meta-tty.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#ifndef META_TTY_H
+#define META_TTY_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define META_TYPE_TTY              (meta_tty_get_type())
+#define META_TTY(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_TTY, MetaTTY))
+#define META_TTY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_TTY, MetaTTYClass))
+#define META_IS_TTY(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_TTY))
+#define META_IS_TTY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_TTY))
+#define META_TTY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TTY, MetaTTYClass))
+
+typedef struct _MetaTTY      MetaTTY;
+typedef struct _MetaTTYClass MetaTTYClass;
+
+GType             meta_tty_get_type                (void) G_GNUC_CONST;
+
+MetaTTY          *meta_tty_new                     (void);
+
+G_END_DECLS
+
+#endif /* META_TTY_H */
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index a859378..7d111ec 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -28,6 +28,7 @@
 #include <cairo.h>
 
 #include "window-private.h"
+#include "meta-tty.h"
 
 typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
 
@@ -165,6 +166,10 @@ struct _MetaWaylandCompositor
   struct wl_client *xwayland_client;
   struct wl_resource *xserver_resource;
 
+  MetaTTY *tty;
+  int drm_fd;
+  GSocket *weston_launch;
+
   MetaWaylandSeat *seat;
 
   /* This surface is only used to keep drag of the implicit grab when
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 190a7c4..9070d80 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -48,6 +48,7 @@
 #include <meta/types.h>
 #include <meta/main.h>
 #include "frame.h"
+#include "meta-weston-launch.h"
 
 static MetaWaylandCompositor _meta_wayland_compositor;
 
@@ -1394,11 +1395,59 @@ event_emission_hook_cb (GSignalInvocationHint *ihint,
   return TRUE /* stay connected */;
 }
 
+static int
+env_get_fd (const char *env)
+{
+  const char *value;
+
+  value = g_getenv (env);
+
+  if (value == NULL)
+    return -1;
+  else
+    return g_ascii_strtoll (env, NULL, 10);
+}
+
+static void
+on_our_vt_enter (MetaTTY               *tty,
+                MetaWaylandCompositor *compositor)
+{
+  GError *error;
+
+  error = NULL;
+  if (!meta_weston_launch_set_master (compositor->weston_launch,
+                                     compositor->drm_fd, TRUE, &error))
+    {
+      g_warning ("Failed to become DRM master: %s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+on_our_vt_leave (MetaTTY               *tty,
+                MetaWaylandCompositor *compositor)
+{
+  GError *error;
+
+  error = NULL;
+  if (!meta_weston_launch_set_master (compositor->weston_launch,
+                                     compositor->drm_fd, FALSE, &error))
+    {
+      g_warning ("Failed to release DRM master: %s", error->message);
+      g_error_free (error);
+    }
+
+  /* FIXME: we must release input devices as well! */
+}
+
 void
 meta_wayland_init (void)
 {
   MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
   guint event_signal;
+  ClutterBackend *backend;
+  CoglContext *cogl_context;
+  CoglRenderer *cogl_renderer;
 
   memset (compositor, 0, sizeof (MetaWaylandCompositor));
 
@@ -1438,6 +1487,34 @@ meta_wayland_init (void)
   if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
     g_error ("Failed to initialize Clutter");
 
+  backend = clutter_get_default_backend ();
+  cogl_context = clutter_backend_get_cogl_context (backend);
+  cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
+
+  if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS)
+    compositor->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
+  else
+    compositor->drm_fd = -1;
+
+  if (compositor->drm_fd >= 0)
+    {
+      /* Running on bare metal, let's initalize DRM master and VT handling */
+      int weston_launch_fd;
+
+      weston_launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
+      if (weston_launch_fd >= 0)
+       compositor->weston_launch = g_socket_new_from_fd (weston_launch_fd, NULL);
+
+      compositor->tty = meta_tty_new ();
+      if (compositor->tty)
+       {
+         g_signal_connect (compositor->tty, "enter", G_CALLBACK (on_our_vt_enter), compositor);
+         g_signal_connect (compositor->tty, "leave", G_CALLBACK (on_our_vt_leave), compositor);
+       }
+
+      on_our_vt_enter (compositor->tty, compositor);
+    }
+
   compositor->stage = meta_wayland_stage_new ();
   clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
   g_signal_connect_after (compositor->stage, "paint",
@@ -1495,5 +1572,10 @@ meta_wayland_init (void)
 void
 meta_wayland_finalize (void)
 {
-  meta_xwayland_stop (meta_wayland_compositor_get_default ());
+  MetaWaylandCompositor *compositor;
+
+  compositor = meta_wayland_compositor_get_default ();
+
+  meta_xwayland_stop (compositor);
+  g_clear_object (&compositor->tty);
 }
diff --git a/src/wayland/meta-weston-launch.c b/src/wayland/meta-weston-launch.c
new file mode 100644
index 0000000..68f14cf
--- /dev/null
+++ b/src/wayland/meta-weston-launch.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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 <config.h>
+
+#include <gio/gio.h>
+#include <gio/gunixfdmessage.h>
+
+#include <glib.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "meta-weston-launch.h"
+
+static gboolean
+send_message_to_wl (GSocket                *weston_launch,
+                   void                   *message,
+                   gsize                   size,
+                   GSocketControlMessage  *out_cmsg,
+                   GSocketControlMessage **in_cmsg,
+                   GError                **error)
+{
+  int ok;
+  GInputVector in_iov = { &ok, sizeof (int) };
+  GOutputVector out_iov = { message, size };
+  GSocketControlMessage *out_all_cmsg[2];
+  GSocketControlMessage **in_all_cmsg;
+  int flags = 0;
+  int i;
+
+  out_all_cmsg[0] = out_cmsg;
+  out_all_cmsg[1] = NULL;
+  if (g_socket_send_message (weston_launch, NULL,
+                            &out_iov, 1,
+                            out_all_cmsg, -1,
+                            flags, NULL, error) != (gssize)size)
+    return FALSE;
+
+  if (g_socket_receive_message (weston_launch, NULL,
+                               &in_iov, 1,
+                               &in_all_cmsg, NULL,
+                               &flags, NULL, error) != sizeof (int))
+    return FALSE;
+
+  if (ok != 0)
+    {
+      if (ok == -1)
+       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                    "Got failure from weston-launch");
+      else
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ok),
+                    "Got failure from weston-launch: %s", strerror (-ok));
+
+      for (i = 0; in_all_cmsg[i]; i++)
+       g_object_unref (in_all_cmsg[i]);
+      g_free (in_all_cmsg);
+
+      return FALSE;
+    }
+
+  if (in_all_cmsg[0])
+    {
+      for (i = 1; in_all_cmsg[i]; i++)
+       g_object_unref (in_all_cmsg[i]);
+      *in_cmsg = in_all_cmsg[0];
+    }
+
+  g_free (in_all_cmsg);
+  return TRUE;
+}          
+
+gboolean
+meta_weston_launch_set_master (GSocket   *weston_launch,
+                              int        drm_fd,
+                              gboolean   master,
+                              GError   **error)
+{
+  if (weston_launch)
+    {
+      struct weston_launcher_set_master message;
+      GSocketControlMessage *cmsg;
+      gboolean ok;
+
+      message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER;
+      message.set_master = master;
+
+      cmsg = g_unix_fd_message_new ();
+      if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg),
+                                      drm_fd, error) == FALSE)
+       {
+         g_object_unref (cmsg);
+         return FALSE;
+       }
+
+      ok = send_message_to_wl (weston_launch, &message, sizeof message, cmsg, NULL, error);
+
+      g_object_unref (cmsg);
+      return ok;
+    }
+  else
+    {
+      int ret;
+
+      if (master)
+       ret = drmSetMaster (drm_fd);
+      else
+       ret = drmDropMaster (drm_fd);
+
+      if (ret < 0)
+       {
+         g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                      "Failed to set DRM master directly: %s", strerror (-ret));
+         return FALSE;
+       }
+      else
+       return TRUE;
+    }
+}
+
+int
+meta_weston_launch_open_input_device (GSocket    *weston_launch,
+                                     const char *name,
+                                     int         flags,
+                                     GError    **error)
+{
+  if (weston_launch)
+    {
+      struct weston_launcher_open *message;
+      GSocketControlMessage *cmsg;
+      gboolean ok;
+      int *fds, n_fd;
+      int ret;
+
+      message = g_malloc (sizeof (struct weston_launcher_open) +
+                         strlen (name));
+      message->header.opcode = WESTON_LAUNCHER_OPEN;
+      message->flags = flags;
+      strcpy (message->path, name);
+
+      ok = send_message_to_wl (weston_launch, message,
+                              sizeof (struct weston_launcher_open) + strlen (name),
+                              NULL, &cmsg, error);
+
+      if (ok)
+       {
+         g_assert (G_IS_UNIX_FD_MESSAGE (cmsg));
+
+         fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd);
+         g_assert (n_fd == 1);
+
+         ret = fds[0];
+         g_free (fds);
+       }
+      else
+       ret = -1;
+
+      g_free (message);
+      g_object_unref (cmsg);
+      return ret;
+    }
+  else
+    {
+      int ret;
+
+      ret = open (name, flags, 0);
+
+      if (ret < 0)
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                    "Failed to open input device directly: %s", strerror (errno));
+
+      return ret;
+    }
+}
+
diff --git a/src/wayland/meta-weston-launch.h b/src/wayland/meta-weston-launch.h
new file mode 100644
index 0000000..f642ba9
--- /dev/null
+++ b/src/wayland/meta-weston-launch.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#ifndef META_WESTON_LAUNCH_H
+#define META_WESTON_LAUNCH_H
+
+#include <glib-object.h>
+
+/* Keep this in sync with weston-launch */
+
+enum weston_launcher_opcode {
+       WESTON_LAUNCHER_OPEN,
+       WESTON_LAUNCHER_DRM_SET_MASTER
+};
+
+struct weston_launcher_message {
+       int opcode;
+};
+
+struct weston_launcher_open {
+       struct weston_launcher_message header;
+       int flags;
+       char path[0];
+};
+
+struct weston_launcher_set_master {
+       struct weston_launcher_message header;
+       int set_master;
+};
+
+gboolean meta_weston_launch_set_master (GSocket   *weston_launch,
+                                       int        drm_fd,
+                                       gboolean   master,
+                                       GError   **error);
+int      meta_weston_launch_open_input_device (GSocket     *weston_launch,
+                                              const char  *name,
+                                              int          flags,
+                                              GError     **error);
+
+#endif
diff --git a/src/wayland/weston-launch.c b/src/wayland/weston-launch.c
new file mode 100644
index 0000000..b38386e
--- /dev/null
+++ b/src/wayland/weston-launch.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <error.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/signalfd.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <termios.h>
+#include <linux/vt.h>
+#include <linux/major.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_appl.h>
+
+#include <xf86drm.h>
+
+#ifdef HAVE_SYSTEMD_LOGIN
+#include <systemd/sd-login.h>
+#endif
+
+#include "weston-launch.h"
+
+#define MAX_ARGV_SIZE 256
+
+struct weston_launch {
+       struct pam_conv pc;
+       pam_handle_t *ph;
+       int tty;
+       int ttynr;
+       int sock[2];
+       struct passwd *pw;
+
+       int signalfd;
+
+       pid_t child;
+       int verbose;
+       char *new_user;
+};
+
+union cmsg_data { unsigned char b[4]; int fd; };
+
+static gid_t *
+read_groups(void)
+{
+       int n;
+       gid_t *groups;
+       
+       n = getgroups(0, NULL);
+
+       if (n < 0) {
+               fprintf(stderr, "Unable to retrieve groups: %m\n");
+               return NULL;
+       }
+
+       groups = malloc(n * sizeof(gid_t));
+       if (!groups)
+               return NULL;
+
+       if (getgroups(n, groups) < 0) {
+               fprintf(stderr, "Unable to retrieve groups: %m\n");
+               free(groups);
+               return NULL;
+       }
+       return groups;
+}
+
+static int
+weston_launch_allowed(struct weston_launch *wl)
+{
+       struct group *gr;
+       gid_t *groups;
+       int i;
+#ifdef HAVE_SYSTEMD_LOGIN
+       char *session, *seat;
+       int err;
+#endif
+
+       if (getuid() == 0)
+               return 1;
+
+       gr = getgrnam("weston-launch");
+       if (gr) {
+               groups = read_groups();
+               if (groups) {
+                       for (i = 0; groups[i]; ++i) {
+                               if (groups[i] == gr->gr_gid) {
+                                       free(groups);
+                                       return 1;
+                               }
+                       }
+                       free(groups);
+               }
+       }
+
+#ifdef HAVE_SYSTEMD_LOGIN
+       err = sd_pid_get_session(getpid(), &session);
+       if (err == 0 && session) {
+               if (sd_session_is_active(session) &&
+                   sd_session_get_seat(session, &seat) == 0) {
+                       free(seat);
+                       free(session);
+                       return 1;
+               }
+               free(session);
+       }
+#endif
+       
+       return 0;
+}
+
+static int
+pam_conversation_fn(int msg_count,
+                   const struct pam_message **messages,
+                   struct pam_response **responses,
+                   void *user_data)
+{
+       return PAM_SUCCESS;
+}
+
+static int
+setup_pam(struct weston_launch *wl)
+{
+       int err;
+
+       wl->pc.conv = pam_conversation_fn;
+       wl->pc.appdata_ptr = wl;
+
+       err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
+       if (err != PAM_SUCCESS) {
+               fprintf(stderr, "failed to start pam transaction: %d: %s\n",
+                       err, pam_strerror(wl->ph, err));
+               return -1;
+       }
+
+       err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
+       if (err != PAM_SUCCESS) {
+               fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
+                       err, pam_strerror(wl->ph, err));
+               return -1;
+       }
+
+       err = pam_open_session(wl->ph, 0);
+       if (err != PAM_SUCCESS) {
+               fprintf(stderr, "failed to open pam session: %d: %s\n",
+                       err, pam_strerror(wl->ph, err));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+setup_launcher_socket(struct weston_launch *wl)
+{
+       if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0)
+               error(1, errno, "socketpair failed");
+       
+       fcntl(wl->sock[0], F_SETFD, O_CLOEXEC);
+
+       return 0;
+}
+
+static int
+setup_signals(struct weston_launch *wl)
+{
+       int ret;
+       sigset_t mask;
+       struct sigaction sa;
+
+       memset(&sa, 0, sizeof sa);
+       sa.sa_handler = SIG_DFL;
+       sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+       ret = sigaction(SIGCHLD, &sa, NULL);
+       assert(ret == 0);
+
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = 0;
+       sigaction(SIGHUP, &sa, NULL);
+
+       ret = sigemptyset(&mask);
+       assert(ret == 0);
+       sigaddset(&mask, SIGCHLD);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+       ret = sigprocmask(SIG_BLOCK, &mask, NULL);
+       assert(ret == 0);
+
+       wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+       if (wl->signalfd < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void
+setenv_fd(const char *env, int fd)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof buf, "%d", fd);
+       setenv(env, buf, 1);
+}
+
+static int
+handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
+{
+       int ret = -1;
+       struct cmsghdr *cmsg;
+       struct weston_launcher_set_master *message;
+       union cmsg_data *data;
+
+       if (len != sizeof(*message)) {
+               error(0, 0, "missing value in setmaster request");
+               goto out;
+       }
+
+       message = msg->msg_iov->iov_base;
+
+       cmsg = CMSG_FIRSTHDR(msg);
+       if (!cmsg ||
+           cmsg->cmsg_level != SOL_SOCKET ||
+           cmsg->cmsg_type != SCM_RIGHTS) {
+               error(0, 0, "invalid control message");
+               goto out;
+       }
+
+       data = (union cmsg_data *) CMSG_DATA(cmsg);
+       if (data->fd == -1) {
+               error(0, 0, "missing drm fd in socket request");
+               goto out;
+       }
+
+       if (message->set_master)
+               ret = drmSetMaster(data->fd);
+       else
+               ret = drmDropMaster(data->fd);
+
+       close(data->fd);
+
+       if (wl->verbose)
+               fprintf(stderr, "weston-launch: %sMaster, ret: %d, fd: %d\n",
+                       message->set_master ? "set" : "drop", ret, data->fd);
+
+out:
+       do {
+               len = send(wl->sock[0], &ret, sizeof ret, 0);
+       } while (len < 0 && errno == EINTR);
+       if (len < 0)
+               return -1;
+
+       return 0;
+}
+
+static int
+handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
+{
+       int fd = -1, ret = -1;
+       char control[CMSG_SPACE(sizeof(fd))];
+       struct cmsghdr *cmsg;
+       struct stat s;
+       struct msghdr nmsg;
+       struct iovec iov;
+       struct weston_launcher_open *message;
+       union cmsg_data *data;
+
+       message = msg->msg_iov->iov_base;
+       if ((size_t)len < sizeof(*message))
+               goto err0;
+
+       /* Ensure path is null-terminated */
+       ((char *) message)[len-1] = '\0';
+
+       if (stat(message->path, &s) < 0)
+               goto err0;
+
+       fd = open(message->path, message->flags);
+       if (fd < 0) {
+               fprintf(stderr, "Error opening device %s: %m\n",
+                       message->path);
+               goto err0;
+       }
+
+       if (major(s.st_rdev) != INPUT_MAJOR) {
+               close(fd);
+               fd = -1;
+               fprintf(stderr, "Device %s is not an input device\n",
+                       message->path);
+               goto err0;
+       }
+
+err0:
+       memset(&nmsg, 0, sizeof nmsg);
+       nmsg.msg_iov = &iov;
+       nmsg.msg_iovlen = 1;
+       if (fd != -1) {
+               nmsg.msg_control = control;
+               nmsg.msg_controllen = sizeof control;
+               cmsg = CMSG_FIRSTHDR(&nmsg);
+               cmsg->cmsg_level = SOL_SOCKET;
+               cmsg->cmsg_type = SCM_RIGHTS;
+               cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+               data = (union cmsg_data *) CMSG_DATA(cmsg);
+               data->fd = fd;
+               nmsg.msg_controllen = cmsg->cmsg_len;
+               ret = 0;
+       }
+       iov.iov_base = &ret;
+       iov.iov_len = sizeof ret;
+
+       if (wl->verbose)
+               fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
+                       message->path, ret, fd);
+       do {
+               len = sendmsg(wl->sock[0], &nmsg, 0);
+       } while (len < 0 && errno == EINTR);
+
+       close(fd);
+
+       if (len < 0)
+               return -1;
+
+       return 0;
+}
+
+static int
+handle_socket_msg(struct weston_launch *wl)
+{
+       char control[CMSG_SPACE(sizeof(int))];
+       char buf[BUFSIZ];
+       struct msghdr msg;
+       struct iovec iov;
+       int ret = -1;
+       ssize_t len;
+       struct weston_launcher_message *message;
+
+       memset(&msg, 0, sizeof(msg));
+       iov.iov_base = buf;
+       iov.iov_len  = sizeof buf;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof control;
+
+       do {
+               len = recvmsg(wl->sock[0], &msg, 0);
+       } while (len < 0 && errno == EINTR);
+
+       if (len < 1)
+               return -1;
+
+       message = (void *) buf;
+       switch (message->opcode) {
+       case WESTON_LAUNCHER_OPEN:
+               ret = handle_open(wl, &msg, len);
+               break;
+       case WESTON_LAUNCHER_DRM_SET_MASTER:
+               ret = handle_setmaster(wl, &msg, len);
+               break;
+       }
+
+       return ret;
+}
+
+static void
+quit(struct weston_launch *wl, int status)
+{
+       int err;
+
+       close(wl->signalfd);
+       close(wl->sock[0]);
+
+       if (wl->new_user) {
+               err = pam_close_session(wl->ph, 0);
+               if (err)
+                       fprintf(stderr, "pam_close_session failed: %d: %s\n",
+                               err, pam_strerror(wl->ph, err));
+               pam_end(wl->ph, err);
+       }
+
+       exit(status);
+}
+
+static int
+handle_signal(struct weston_launch *wl)
+{
+       struct signalfd_siginfo sig;
+       int pid, status, ret;
+
+       if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
+               error(0, errno, "reading signalfd failed");
+               return -1;
+       }
+
+       switch (sig.ssi_signo) {
+       case SIGCHLD:
+               pid = waitpid(-1, &status, 0);
+               if (pid == wl->child) {
+                       wl->child = 0;
+                       if (WIFEXITED(status))
+                               ret = WEXITSTATUS(status);
+                       else if (WIFSIGNALED(status))
+                               /*
+                                * If weston dies because of signal N, we
+                                * return 10+N. This is distinct from
+                                * weston-launch dying because of a signal
+                                * (128+N).
+                                */
+                               ret = 10 + WTERMSIG(status);
+                       else
+                               ret = 0;
+                       quit(wl, ret);
+               }
+               break;
+       case SIGTERM:
+       case SIGINT:
+               if (wl->child)
+                       kill(wl->child, sig.ssi_signo);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+setup_tty(struct weston_launch *wl, const char *tty)
+{
+       struct stat buf;
+       char *t;
+
+       if (!wl->new_user) {
+               wl->tty = STDIN_FILENO;
+       } else if (tty) {
+               t = ttyname(STDIN_FILENO);
+               if (t && strcmp(t, tty) == 0)
+                       wl->tty = STDIN_FILENO;
+               else
+                       wl->tty = open(tty, O_RDWR | O_NOCTTY);
+       } else {
+               int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
+               char filename[16];
+
+               if (tty0 < 0)
+                       error(1, errno, "could not open tty0");
+
+               if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
+                       error(1, errno, "failed to find non-opened console"); 
+
+               snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
+               wl->tty = open(filename, O_RDWR | O_NOCTTY);
+               close(tty0);
+       }
+
+       if (wl->tty < 0)
+               error(1, errno, "failed to open tty");
+
+       if (tty) {
+               if (fstat(wl->tty, &buf) < 0)
+                       error(1, errno, "stat %s failed", tty);
+
+               if (major(buf.st_rdev) != TTY_MAJOR)
+                       error(1, 0, "invalid tty device: %s", tty);
+
+               wl->ttynr = minor(buf.st_rdev);
+       }
+
+       return 0;
+}
+
+static void
+setup_session(struct weston_launch *wl)
+{
+       char **env;
+       char *term;
+       int i;
+
+       if (wl->tty != STDIN_FILENO) {
+               if (setsid() < 0)
+                       error(1, errno, "setsid failed");
+               if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
+                       error(1, errno, "TIOCSCTTY failed - tty is in use");
+       }
+
+       if (setgid(wl->pw->pw_gid) < 0 ||
+#ifdef HAVE_INITGROUPS
+           initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
+#endif
+           setuid(wl->pw->pw_uid) < 0)
+               error(1, errno, "dropping privileges failed");
+
+       term = getenv("TERM");
+       clearenv();
+       setenv("TERM", term, 1);
+       setenv("USER", wl->pw->pw_name, 1);
+       setenv("LOGNAME", wl->pw->pw_name, 1);
+       setenv("HOME", wl->pw->pw_dir, 1);
+       setenv("SHELL", wl->pw->pw_shell, 1);
+
+       env = pam_getenvlist(wl->ph);
+       if (env) {
+               for (i = 0; env[i]; ++i) {
+                       if (putenv(env[i]) < 0)
+                               error(0, 0, "putenv %s failed", env[i]);
+               }
+               free(env);
+       }
+}
+
+static void
+launch_compositor(struct weston_launch *wl, int argc, char *argv[])
+{
+       char command[PATH_MAX];
+       char *child_argv[MAX_ARGV_SIZE];
+       sigset_t mask;
+       int i;
+
+       if (wl->verbose)
+               printf("weston-launch: spawned weston with pid: %d\n", getpid());
+       if (wl->new_user)
+               setup_session(wl);
+
+       if (wl->tty != STDIN_FILENO)
+               setenv_fd("WESTON_TTY_FD", wl->tty);
+
+       setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
+
+       unsetenv("DISPLAY");
+
+       /* Do not give our signal mask to the new process. */
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGTERM);
+       sigaddset(&mask, SIGCHLD);
+       sigaddset(&mask, SIGINT);
+       sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+       snprintf (command, PATH_MAX, "%s \"$ \"", argv[0]);
+
+       child_argv[0] = wl->pw->pw_shell;
+       child_argv[1] = "-l";
+       child_argv[2] = "-c";
+       child_argv[3] = command;
+       for (i = 0; i < argc; ++i)
+               child_argv[4 + i] = argv[i];
+       child_argv[4 + i] = NULL;
+
+       execv(child_argv[0], child_argv);
+       error(1, errno, "exec failed");
+}
+
+static void
+help(const char *name)
+{
+       fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
+       fprintf(stderr, "  -u, --user      Start session as specified username\n");
+       fprintf(stderr, "  -t, --tty       Start session on alternative tty\n");
+       fprintf(stderr, "  -v, --verbose   Be verbose\n");
+       fprintf(stderr, "  -h, --help      Display this help message\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct weston_launch wl;
+       int i, c;
+       char *tty = NULL;
+       struct option opts[] = {
+               { "user",    required_argument, NULL, 'u' },
+               { "tty",     required_argument, NULL, 't' },
+               { "verbose", no_argument,       NULL, 'v' },
+               { "help",    no_argument,       NULL, 'h' },
+               { 0,         0,                 NULL,  0  }
+       };      
+
+       memset(&wl, 0, sizeof wl);
+
+       while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
+               switch (c) {
+               case 'u':
+                       wl.new_user = optarg;
+                       if (getuid() != 0)
+                               error(1, 0, "Permission denied. -u allowed for root only");
+                       break;
+               case 't':
+                       tty = optarg;
+                       break;
+               case 'v':
+                       wl.verbose = 1;
+                       break;
+               case 'h':
+                       help("weston-launch");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       if ((argc - optind) > (MAX_ARGV_SIZE - 6))
+               error(1, E2BIG, "Too many arguments to pass to weston");
+
+       if (strcmp (argv[optind], "mutter") &&
+           strcmp (argv[optind], "gnome-shell") &&
+           strcmp (argv[optind], "gnome-shell-real"))
+               error(1, 0, "mutter-launch can only be used to launch mutter or gnome-shell");
+
+       if (wl.new_user)
+               wl.pw = getpwnam(wl.new_user);
+       else
+               wl.pw = getpwuid(getuid());
+       if (wl.pw == NULL)
+               error(1, errno, "failed to get username");
+
+       if (!weston_launch_allowed(&wl))
+               error(1, 0, "Permission denied. You should either:\n"
+#ifdef HAVE_SYSTEMD_LOGIN
+                     " - run from an active and local (systemd) session.\n"
+#else
+                     " - enable systemd session support for weston-launch.\n"
+#endif
+                     " - or add yourself to the 'weston-launch' group.");
+
+       if (setup_tty(&wl, tty) < 0)
+               exit(EXIT_FAILURE);
+
+       if (wl.new_user && setup_pam(&wl) < 0)
+               exit(EXIT_FAILURE);
+
+       if (setup_launcher_socket(&wl) < 0)
+               exit(EXIT_FAILURE);
+
+       if (setup_signals(&wl) < 0)
+               exit(EXIT_FAILURE);
+
+       wl.child = fork();
+       if (wl.child == -1) {
+               error(1, errno, "fork failed");
+               exit(EXIT_FAILURE);
+       }
+
+       if (wl.child == 0)
+               launch_compositor(&wl, argc - optind, argv + optind);
+
+       close(wl.sock[1]);
+       if (wl.tty != STDIN_FILENO)
+               close(wl.tty);
+
+       while (1) {
+               struct pollfd fds[2];
+               int n;
+
+               fds[0].fd = wl.sock[0];
+               fds[0].events = POLLIN;
+               fds[1].fd = wl.signalfd;
+               fds[1].events = POLLIN;
+
+               n = poll(fds, 2, -1);
+               if (n < 0)
+                       error(0, errno, "poll failed");
+               if (fds[0].revents & POLLIN)
+                       handle_socket_msg(&wl);
+               if (fds[1].revents)
+                       handle_signal(&wl);
+       }
+
+       return 0;
+}
diff --git a/src/wayland/weston-launch.h b/src/wayland/weston-launch.h
new file mode 100644
index 0000000..5be013e
--- /dev/null
+++ b/src/wayland/weston-launch.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WESTON_LAUNCH_H_
+#define _WESTON_LAUNCH_H_
+
+enum weston_launcher_opcode {
+       WESTON_LAUNCHER_OPEN,
+       WESTON_LAUNCHER_DRM_SET_MASTER
+};
+
+struct weston_launcher_message {
+       int opcode;
+};
+
+struct weston_launcher_open {
+       struct weston_launcher_message header;
+       int flags;
+       char path[0];
+};
+
+struct weston_launcher_set_master {
+       struct weston_launcher_message header;
+       int set_master;
+};
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]