From dcf64ca1678a2950842708bee146f09a063ed828 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Tue, 31 Dec 2013 17:44:45 -0500 Subject: [PATCH] launcher: Replace mutter-launch with logind integration This uses David Herrmann's new logind sessions interface to retrieve fds for input devices, rather than using a custom setuid helper to do the management. This vastly simplifies the interface. This requires systemd v210, at least. https://bugzilla.gnome.org/show_bug.cgi?id=724604 --- .gitignore | 2 +- configure.ac | 3 +- src/Makefile.am | 25 +- src/backends/native/dbus-utils.c | 107 +++++ src/backends/native/dbus-utils.h | 32 ++ src/backends/native/meta-launcher.c | 441 +++++++++-------- src/backends/native/weston-launch.c | 711 ---------------------------- src/backends/native/weston-launch.h | 68 --- src/mutter-wayland.desktop.in | 2 +- src/org.freedesktop.login1.xml | 44 ++ 10 files changed, 410 insertions(+), 1025 deletions(-) create mode 100644 src/backends/native/dbus-utils.c create mode 100644 src/backends/native/dbus-utils.h delete mode 100644 src/backends/native/weston-launch.c delete mode 100644 src/backends/native/weston-launch.h create mode 100644 src/org.freedesktop.login1.xml diff --git a/.gitignore b/.gitignore index 1b238e149..446c09233 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,6 @@ po/*.pot 50-metacity-key.xml libmutter.pc mutter -mutter-launch org.gnome.mutter.gschema.valid org.gnome.mutter.gschema.xml org.gnome.mutter.wayland.gschema.valid @@ -78,6 +77,7 @@ src/mutter-marshal.[ch] src/stamp-mutter-marshal.h src/meta-dbus-display-config.[ch] src/meta-dbus-idle-monitor.[ch] +src/meta-dbus-login1.[ch] src/gtk-shell-protocol.c src/gtk-shell-server-protocol.h src/xdg-shell-protocol.c diff --git a/configure.ac b/configure.ac index 7a8d1b9a7..c8c92f332 100644 --- a/configure.ac +++ b/configure.ac @@ -127,7 +127,6 @@ AM_GLIB_GNU_GETTEXT ## here we get the flags we'll actually use # GRegex requires Glib-2.14.0 PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.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" @@ -200,7 +199,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"], AC_SUBST([WAYLAND_SCANNER]) AC_SUBST(XWAYLAND_PATH) -MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server >= 1.4.93 libdrm" +MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server >= 1.4.93 libdrm libsystemd" PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES) PKG_CHECK_EXISTS([xi >= 1.6.99.1], diff --git a/src/Makefile.am b/src/Makefile.am index 5e6b710f4..c71697f7b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,6 +35,7 @@ INCLUDES= \ mutter_built_sources = \ $(dbus_idle_built_sources) \ $(dbus_display_config_built_sources) \ + $(dbus_login1_built_sources) \ mutter-enum-types.h \ mutter-enum-types.c \ gtk-shell-protocol.c \ @@ -80,6 +81,8 @@ libmutter_la_SOURCES = \ backends/native/meta-monitor-manager-kms.h \ backends/native/meta-launcher.c \ backends/native/meta-launcher.h \ + backends/native/dbus-utils.c \ + backends/native/dbus-utils.h \ backends/x11/meta-backend-x11.c \ backends/x11/meta-backend-x11.h \ backends/x11/meta-cursor-renderer-x11.c \ @@ -293,19 +296,6 @@ bin_PROGRAMS=mutter mutter_SOURCES = core/mutter.c mutter_LDADD = $(MUTTER_LIBS) libmutter.la -bin_PROGRAMS+=mutter-launch - -mutter_launch_SOURCES = \ - backends/native/weston-launch.c \ - backends/native/weston-launch.h - -mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS) -DLIBDIR=\"$(libdir)\" -mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam - -install-exec-hook: - -chown root $(DESTDIR)$(bindir)/mutter-launch - -chmod u+s $(DESTDIR)$(bindir)/mutter-launch - if HAVE_INTROSPECTION include $(INTROSPECTION_MAKEFILE) @@ -442,6 +432,15 @@ $(dbus_idle_built_sources) : Makefile.am org.gnome.Mutter.IdleMonitor.xml --c-generate-object-manager \ $(srcdir)/org.gnome.Mutter.IdleMonitor.xml +dbus_login1_built_sources = meta-dbus-login1.c meta-dbus-login1.h + +$(dbus_login1_built_sources) : Makefile.am org.freedesktop.login1.xml + $(AM_V_GEN)gdbus-codegen \ + --interface-prefix org.freedesktop.login1 \ + --c-namespace Login1 \ + --generate-c-code meta-dbus-login1 \ + $(srcdir)/org.freedesktop.login1.xml + %-protocol.c : $(srcdir)/wayland/protocol/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@ %-server-protocol.h : $(srcdir)/wayland/protocol/%.xml diff --git a/src/backends/native/dbus-utils.c b/src/backends/native/dbus-utils.c new file mode 100644 index 000000000..6a63b92e8 --- /dev/null +++ b/src/backends/native/dbus-utils.c @@ -0,0 +1,107 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2014 Red Hat + * + * 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. + * + * Written by: + * Jasper St. Pierre + */ + +#include "config.h" + +#include "dbus-utils.h" + +#include + +/* Stolen from tp_escape_as_identifier, from tp-glib, + * which follows the same escaping convention as systemd. + */ +static inline gboolean +_esc_ident_bad (gchar c, gboolean is_first) +{ + return ((c < 'a' || c > 'z') && + (c < 'A' || c > 'Z') && + (c < '0' || c > '9' || is_first)); +} + +static gchar * +escape_dbus_component (const gchar *name) +{ + gboolean bad = FALSE; + size_t len = 0; + GString *op; + const gchar *ptr, *first_ok; + + g_return_val_if_fail (name != NULL, NULL); + + /* fast path for empty name */ + if (name[0] == '\0') + return g_strdup ("_"); + + for (ptr = name; *ptr; ptr++) + { + if (_esc_ident_bad (*ptr, ptr == name)) + { + bad = TRUE; + len += 3; + } + else + len++; + } + + /* fast path if it's clean */ + if (!bad) + return g_strdup (name); + + /* If strictly less than ptr, first_ok is the first uncopied safe character. + */ + first_ok = name; + op = g_string_sized_new (len); + for (ptr = name; *ptr; ptr++) + { + if (_esc_ident_bad (*ptr, ptr == name)) + { + /* copy preceding safe characters if any */ + if (first_ok < ptr) + { + g_string_append_len (op, first_ok, ptr - first_ok); + } + /* escape the unsafe character */ + g_string_append_printf (op, "_%02x", (unsigned char)(*ptr)); + /* restart after it */ + first_ok = ptr + 1; + } + } + /* copy trailing safe characters if any */ + if (first_ok < ptr) + { + g_string_append_len (op, first_ok, ptr - first_ok); + } + return g_string_free (op, FALSE); +} + +char * +get_escaped_dbus_path (const char *prefix, + const char *component) +{ + char *escaped_component = escape_dbus_component (component); + char *path = g_strconcat (prefix, "/", escaped_component, NULL); + + g_free (escaped_component); + return path; +} diff --git a/src/backends/native/dbus-utils.h b/src/backends/native/dbus-utils.h new file mode 100644 index 000000000..ee56c455a --- /dev/null +++ b/src/backends/native/dbus-utils.h @@ -0,0 +1,32 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2014 Red Hat + * + * 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. + * + * Written by: + * Jasper St. Pierre + */ + +#ifndef DBUS_UTILS_H +#define DBUS_UTILS_H + +char * +get_escaped_dbus_path (const char *prefix, + const char *component); + +#endif /* DBUS_UTILS_H */ diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c index 66755088e..751919464 100644 --- a/src/backends/native/meta-launcher.c +++ b/src/backends/native/meta-launcher.c @@ -20,9 +20,8 @@ #include "config.h" #include "meta-launcher.h" -#include "weston-launch.h" -#include +#include #include #include @@ -30,10 +29,17 @@ #include #include +#include #include #include #include #include +#include + +#include + +#include "dbus-utils.h" +#include "meta-dbus-login1.h" #include "wayland/meta-wayland-private.h" #include "backends/meta-backend.h" @@ -41,153 +47,44 @@ struct _MetaLauncher { - GSocket *weston_launch; - GSource *weston_launch_source; + Login1Session *session_proxy; + Login1Seat *seat_proxy; - gboolean vt_switched; + gboolean session_active; }; /* AAA BBB CCC */ -static void handle_request_vt_switch (MetaLauncher *self); - -static gboolean -request_vt_switch_idle (gpointer user_data) +static Login1Session * +get_session_proxy (GCancellable *cancellable) { - handle_request_vt_switch (user_data); + char *proxy_path; + char *session_id; + Login1Session *session_proxy; - return FALSE; + if (sd_pid_get_session (getpid (), &session_id) < 0) + return NULL; + + proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id); + + session_proxy = login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.freedesktop.login1", + proxy_path, + cancellable, NULL); + free (proxy_path); + + return session_proxy; } -static gboolean -send_message_to_wl (MetaLauncher *self, - void *message, - gsize size, - GSocketControlMessage *out_cmsg, - GSocketControlMessage **in_cmsg, - GError **error) +static Login1Seat * +get_seat_proxy (GCancellable *cancellable) { - struct weston_launcher_reply reply; - GInputVector in_iov = { &reply, sizeof (reply) }; - 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 (self->weston_launch, NULL, - &out_iov, 1, - out_all_cmsg, -1, - flags, NULL, error) != (gssize)size) - return FALSE; - - if (g_socket_receive_message (self->weston_launch, NULL, - &in_iov, 1, - &in_all_cmsg, NULL, - &flags, NULL, error) != sizeof (reply)) - return FALSE; - - while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode) - { - guint id; - - /* There were events queued */ - g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT); - - /* This can never happen, because the only time mutter-launch can queue - this event is after confirming a VT switch, and we don't make requests - during that time. - - Note that getting this event would be really bad, because we would be - in the wrong loop/context. - */ - g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER); - - switch (reply.header.opcode) - { - case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: - id = g_idle_add (request_vt_switch_idle, self); - g_source_set_name_by_id (id, "[mutter] request_vt_switch_idle"); - break; - - default: - g_assert_not_reached (); - } - - if (g_socket_receive_message (self->weston_launch, NULL, - &in_iov, 1, - NULL, NULL, - &flags, NULL, error) != sizeof (reply)) - return FALSE; - } - - if (reply.ret != 0) - { - if (reply.ret == -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 (-reply.ret), - "Got failure from weston-launch: %s", strerror (-reply.ret)); - - for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++) - g_object_unref (in_all_cmsg[i]); - g_free (in_all_cmsg); - - return FALSE; - } - - if (in_all_cmsg && 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; -} - -static int -meta_launcher_open_device (MetaLauncher *self, - const char *name, - int flags, - GError **error) -{ - struct weston_launcher_open *message; - GSocketControlMessage *cmsg; - gboolean ok; - gsize size; - int *fds, n_fd; - int ret; - - size = sizeof (struct weston_launcher_open) + strlen (name) + 1; - message = g_malloc (size); - message->header.opcode = WESTON_LAUNCHER_OPEN; - message->flags = flags; - strcpy (message->path, name); - message->path[strlen(name)] = 0; - - ok = send_message_to_wl (self, message, size, 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); - g_object_unref (cmsg); - } - else - ret = -1; - - g_free (message); - return ret; + return login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.freedesktop.login1", + "/org/freedesktop/login1/seat/self", + cancellable, NULL); } /* QQQ RRR SSS */ @@ -228,155 +125,246 @@ session_pause (void) clutter_egl_freeze_master_clock (); } +static gboolean +take_device (Login1Session *session_proxy, + int dev_major, + int dev_minor, + int *out_fd, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GVariant *fd_variant = NULL; + int fd = -1; + GUnixFDList *fd_list; + + if (!login1_session_call_take_device_sync (session_proxy, + dev_major, + dev_minor, + NULL, + &fd_variant, + NULL, /* paused */ + &fd_list, + cancellable, + error)) + goto out; + + fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error); + if (fd == -1) + goto out; + + *out_fd = fd; + ret = TRUE; + + out: + if (fd_variant) + g_variant_unref (fd_variant); + if (fd_list) + g_object_unref (fd_list); + return ret; +} + +static gboolean +get_device_info_from_path (const char *path, + int *out_major, + int *out_minor) +{ + gboolean ret = FALSE; + int r; + struct stat st; + + r = stat (path, &st); + if (r < 0) + goto out; + if (!S_ISCHR (st.st_mode)) + goto out; + + *out_major = major (st.st_rdev); + *out_minor = minor (st.st_rdev); + ret = TRUE; + + out: + return ret; +} + +static gboolean +get_device_info_from_fd (int fd, + int *out_major, + int *out_minor) +{ + gboolean ret = FALSE; + int r; + struct stat st; + + r = fstat (fd, &st); + if (r < 0) + goto out; + if (!S_ISCHR (st.st_mode)) + goto out; + + *out_major = major (st.st_rdev); + *out_minor = minor (st.st_rdev); + ret = TRUE; + + out: + return ret; +} + static int on_evdev_device_open (const char *path, int flags, gpointer user_data, GError **error) { - MetaLauncher *launcher = user_data; + MetaLauncher *self = user_data; + int fd; + int major, minor; - return meta_launcher_open_device (launcher, path, flags, error); + if (!get_device_info_from_path (path, &major, &minor)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Could not get device info for path %s: %m", path); + return -1; + } + + if (!take_device (self->session_proxy, major, minor, &fd, NULL, error)) + return -1; + + return fd; } static void on_evdev_device_close (int fd, gpointer user_data) { - close (fd); + MetaLauncher *self = user_data; + int major, minor; + GError *error = NULL; + + if (!get_device_info_from_fd (fd, &major, &minor)) + { + g_warning ("Could not get device info for fd %d: %m", fd); + return; + } + + if (!login1_session_call_release_device_sync (self->session_proxy, + major, minor, + NULL, &error)) + { + g_warning ("Could not release device %d,%d: %s", major, minor, error->message); + } } /* TTT UUU VVV */ static void -handle_vt_enter (MetaLauncher *launcher) +sync_active (MetaLauncher *self) { - g_assert (launcher->vt_switched); - launcher->vt_switched = FALSE; + gboolean active = login1_session_get_active (LOGIN1_SESSION (self->session_proxy)); - session_unpause (); + if (active == self->session_active) + return; + + self->session_active = active; + + if (active) + session_unpause (); + else + session_pause (); } static void -handle_request_vt_switch (MetaLauncher *launcher) +on_active_changed (Login1Session *session, + GParamSpec *pspec, + gpointer user_data) { - struct weston_launcher_message message; - GError *error; - gboolean ok; - - session_pause (); - - message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; - - error = NULL; - ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error); - if (!ok) { - g_warning ("Failed to acknowledge VT switch: %s", error->message); - g_error_free (error); - return; - } - - g_assert (!launcher->vt_switched); - launcher->vt_switched = TRUE; - - session_unpause (); + MetaLauncher *self = user_data; + sync_active (self); } static gboolean -on_socket_readable (GSocket *socket, - GIOCondition condition, - gpointer user_data) +get_kms_fd (Login1Session *session_proxy, + int *fd_out) { - MetaLauncher *launcher = user_data; - struct weston_launcher_event event; - gssize read; - GError *error; + int major, minor; + int fd; + GError *error = NULL; - if ((condition & G_IO_IN) == 0) - return TRUE; - - error = NULL; - read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error); - if (read < (gssize)sizeof(event)) + /* XXX -- use udev to find the DRM master device */ + if (!get_device_info_from_path ("/dev/dri/card0", &major, &minor)) { - g_warning ("Error reading from weston-launcher socket: %s", error->message); + g_warning ("Could not stat /dev/dri/card0: %m"); + return FALSE; + } + + if (!take_device (session_proxy, major, minor, &fd, NULL, &error)) + { + g_warning ("Could not open DRM device: %s\n", error->message); g_error_free (error); - return TRUE; + return FALSE; } - switch (event.header.opcode) - { - case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: - handle_request_vt_switch (launcher); - break; - - case WESTON_LAUNCHER_SERVER_VT_ENTER: - handle_vt_enter (launcher); - break; - } + *fd_out = fd; return TRUE; } -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 (value, NULL, 10); -} - /* XXX YYY ZZZ */ MetaLauncher * meta_launcher_new (void) { - MetaLauncher *self = g_slice_new0 (MetaLauncher); + MetaLauncher *self; + Login1Session *session_proxy; GError *error = NULL; - int launch_fd; int kms_fd; - launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK"); - if (launch_fd < 0) - g_error ("Invalid mutter-launch socket"); + session_proxy = get_session_proxy (NULL); + if (!login1_session_call_take_control_sync (session_proxy, FALSE, NULL, &error)) + { + g_warning ("Could not take control: %s", error->message); + g_error_free (error); + return NULL; + } - self->weston_launch = g_socket_new_from_fd (launch_fd, NULL); + if (!get_kms_fd (session_proxy, &kms_fd)) + return NULL; - self->weston_launch_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); - g_source_set_callback (self->weston_launch_source, (GSourceFunc)on_socket_readable, self, NULL); - g_source_attach (self->weston_launch_source, NULL); - g_source_unref (self->weston_launch_source); + self = g_slice_new0 (MetaLauncher); + self->session_proxy = session_proxy; + self->seat_proxy = get_seat_proxy (NULL); - kms_fd = meta_launcher_open_device (self, "/dev/dri/card0", O_RDWR, &error); - if (error) - g_error ("Failed to open /dev/dri/card0: %s", error->message); + self->session_active = TRUE; clutter_egl_set_kms_fd (kms_fd); clutter_evdev_set_device_callbacks (on_evdev_device_open, on_evdev_device_close, self); + g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self); + return self; } void -meta_launcher_free (MetaLauncher *launcher) +meta_launcher_free (MetaLauncher *self) { - g_source_destroy (launcher->weston_launch_source); - g_object_unref (launcher->weston_launch); - g_slice_free (MetaLauncher, launcher); + g_object_unref (self->seat_proxy); + g_object_unref (self->session_proxy); + g_slice_free (MetaLauncher, self); } gboolean meta_launcher_activate_session (MetaLauncher *launcher, GError **error) { - return meta_launcher_activate_vt (launcher, -1, error); + if (!login1_session_call_activate_sync (launcher->session_proxy, NULL, error)) + return FALSE; + + sync_active (launcher); + return TRUE; } gboolean @@ -384,10 +372,5 @@ meta_launcher_activate_vt (MetaLauncher *launcher, signed char vt, GError **error) { - struct weston_launcher_activate_vt message; - - message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; - message.vt = vt; - - return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error); + return login1_seat_call_switch_to_sync (launcher->seat_proxy, vt, NULL, error); } diff --git a/src/backends/native/weston-launch.c b/src/backends/native/weston-launch.c deleted file mode 100644 index e94375c68..000000000 --- a/src/backends/native/weston-launch.c +++ /dev/null @@ -1,711 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include "weston-launch.h" - -#define MAX_ARGV_SIZE 256 -#define DRM_MAJOR 226 - -enum vt_state { - VT_HAS_VT, - VT_PENDING_CONFIRM, - VT_NOT_HAVE_VT, -}; - -struct weston_launch { - int tty; - int ttynr; - int sock[2]; - struct passwd *pw; - - int signalfd; - - pid_t child; - int verbose; - - struct termios terminal_attributes; - int kb_mode; - enum vt_state vt_state; - unsigned vt; - - int drm_fd; -}; - -union cmsg_data { unsigned char b[4]; int fd; }; - -static void quit (struct weston_launch *wl, int status); - -static int -weston_launch_allowed(struct weston_launch *wl) -{ - char *session, *seat; - int err; - - if (getuid() == 0) - return 1; - - 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); - } - - 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); - sigaddset(&mask, SIGUSR1); - 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_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len) -{ - struct weston_launcher_reply reply; - - reply.header.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; - reply.ret = -1; - - if (wl->vt_state != VT_PENDING_CONFIRM) { - error(0, 0, "unexpected CONFIRM_VT_SWITCH"); - goto out; - } - - if (wl->drm_fd != -1) { - int ret; - - ret = drmDropMaster(wl->drm_fd); - if (ret < 0) { - fprintf(stderr, "failed to drop DRM master: %m\n"); - } else if (wl->verbose) { - fprintf(stderr, "dropped DRM master for VT switch\n"); - } - } - - wl->vt_state = VT_NOT_HAVE_VT; - ioctl(wl->tty, VT_RELDISP, 1); - - if (wl->verbose) - fprintf(stderr, "mutter-launcher: confirmed VT switch\n"); - - reply.ret = 0; - -out: - do { - len = send(wl->sock[0], &reply, sizeof reply, 0); - } while (len < 0 && errno == EINTR); - if (len < 0) - return -1; - - return 0; -} - -static int -handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len) -{ - struct weston_launcher_reply reply; - struct weston_launcher_activate_vt *message; - unsigned vt; - - reply.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; - reply.ret = -1; - - if (len != sizeof(*message)) { - error(0, 0, "missing value in activate_vt request"); - goto out; - } - - message = msg->msg_iov->iov_base; - - /* Negative values mean that we're activating our own VT */ - if (message->vt > 0) - vt = message->vt; - else - vt = wl->vt; - - reply.ret = ioctl(wl->tty, VT_ACTIVATE, vt); - if (reply.ret < 0) - reply.ret = -errno; - - if (wl->verbose) - fprintf(stderr, "mutter-launch: activate VT, ret: %d\n", reply.ret); - -out: - do { - len = send(wl->sock[0], &reply, sizeof reply, 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) -{ - struct weston_launcher_reply reply; - int fd = -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; - int dev_major; - - reply.header.opcode = WESTON_LAUNCHER_OPEN; - reply.ret = -1; - - 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) { - reply.ret = -errno; - goto err0; - } - - dev_major = major(s.st_rdev); - - if (dev_major != INPUT_MAJOR && - dev_major != DRM_MAJOR) { - fprintf(stderr, "Device %s is not an input or DRM device\n", - message->path); - reply.ret = -EPERM; - goto err0; - } - - if (dev_major == DRM_MAJOR && wl->drm_fd != -1) { - fprintf(stderr, "Already have a DRM device open\n"); - reply.ret = -EPERM; - goto err0; - } - - fd = open(message->path, message->flags); - if (fd < 0) { - fprintf(stderr, "Error opening device %s: %m\n", - message->path); - reply.ret = -errno; - goto err0; - } - - if (dev_major == DRM_MAJOR) { - wl->drm_fd = fd; - } - -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; - reply.ret = 0; - } - iov.iov_base = &reply; - iov.iov_len = sizeof reply; - - if (wl->verbose) - fprintf(stderr, "mutter-launch: opened %s: ret: %d, fd: %d\n", - message->path, reply.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_CONFIRM_VT_SWITCH: - ret = handle_confirm_vt_switch(wl, &msg, len); - break; - case WESTON_LAUNCHER_ACTIVATE_VT: - ret = handle_activate_vt(wl, &msg, len); - break; - } - - return ret; -} - -static void -tty_reset(struct weston_launch *wl) -{ - struct vt_mode mode = { 0 }; - - if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) - fprintf(stderr, "failed to restore keyboard mode: %m\n"); - - if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) - fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n"); - - if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0) - fprintf(stderr, "could not restore terminal to canonical mode\n"); - - mode.mode = VT_AUTO; - if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) - fprintf(stderr, "could not reset vt handling\n"); -} - -static void -quit(struct weston_launch *wl, int status) -{ - if (wl->child > 0) - kill(wl->child, SIGKILL); - - close(wl->signalfd); - close(wl->sock[0]); - - if (wl->drm_fd > 0) - close(wl->drm_fd); - - tty_reset(wl); - - exit(status); -} - -static int -handle_vt_switch(struct weston_launch *wl) -{ - struct weston_launcher_event message; - ssize_t len; - - if (wl->vt_state == VT_HAS_VT) { - wl->vt_state = VT_PENDING_CONFIRM; - message.header.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH; - } else if (wl->vt_state == VT_NOT_HAVE_VT) { - wl->vt_state = VT_HAS_VT; - ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); - - if (wl->drm_fd != -1) { - int ret; - - ret = drmSetMaster(wl->drm_fd); - if (ret < 0) { - fprintf(stderr, "failed to become DRM master: %m\n"); - /* This is very, very bad, and the compositor will crash soon, - but oh well... */ - } else if (wl->verbose) { - fprintf(stderr, "became DRM master after VT switch\n"); - } - } - - message.header.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER; - } else - return -1; - - message.detail = 0; - - do { - len = send(wl->sock[0], &message, sizeof(message), 0); - } while (len < 0 && errno == EINTR); - - return 0; -} - - -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; - case SIGUSR1: - return handle_vt_switch(wl); - default: - return -1; - } - - return 0; -} - -static int -setup_tty(struct weston_launch *wl) -{ - struct stat buf; - struct termios raw_attributes; - struct vt_mode mode = { 0 }; - char *session; - char path[PATH_MAX]; - int ok; - - ok = sd_pid_get_session(getpid(), &session); - if (ok < 0) - error(1, -ok, "could not determine current session"); - - ok = sd_session_get_vt(session, &wl->vt); - if (ok < 0) - error(1, -ok, "could not determine current TTY"); - - snprintf(path, PATH_MAX, "/dev/tty%u", wl->vt); - wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); - - if (wl->tty < 0) - error(1, errno, "failed to open tty"); - - if (fstat(wl->tty, &buf) < 0) - error(1, errno, "stat %s failed", path); - - if (major(buf.st_rdev) != TTY_MAJOR) - error(1, 0, "invalid tty device: %s", path); - - wl->ttynr = minor(buf.st_rdev); - - if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0) - error(1, errno, "could not get terminal attributes"); - - /* Ignore control characters and disable echo */ - raw_attributes = wl->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(wl->tty, TCSANOW, &raw_attributes) < 0) - error(1, errno, "could not put terminal into raw mode"); - - ioctl(wl->tty, KDGKBMODE, &wl->kb_mode); - ok = ioctl(wl->tty, KDSKBMODE, K_OFF); - if (ok < 0) { - ok = ioctl(wl->tty, KDSKBMODE, K_RAW); - if (ok < 0) - error(1, errno, "failed to set keyboard mode on tty"); - } - - ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS); - if (ok < 0) - error(1, errno, "failed to set KD_GRAPHICS mode on tty"); - - wl->vt_state = VT_HAS_VT; - mode.mode = VT_PROCESS; - mode.relsig = SIGUSR1; - mode.acqsig = SIGUSR1; - ok = ioctl(wl->tty, VT_SETMODE, &mode); - if (ok < 0) - error(1, errno, "failed to take control of vt handling"); - - return 0; -} - -static void -drop_privileges(struct weston_launch *wl) -{ - 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"); -} - -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()); - - drop_privileges(wl); - - setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]); - setenv("LD_LIBRARY_PATH", LIBDIR, 1); - unsetenv("DISPLAY"); - - /* Do not give our signal mask to the new process. */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGUSR1); - 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, " -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; - struct option opts[] = { - { "verbose", no_argument, NULL, 'v' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, NULL, 0 } - }; - - memset(&wl, 0, sizeof wl); - wl.drm_fd = -1; - - while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) { - switch (c) { - case 'v': - wl.verbose = 1; - break; - case 'h': - help("mutter-launch"); - exit(EXIT_FAILURE); - } - } - - if ((argc - optind) > (MAX_ARGV_SIZE - 6)) - error(1, E2BIG, "Too many arguments to pass to weston"); - - if (optind >= argc) - error(1, 0, "Expected program argument"); - - 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 must run from an active and local (systemd) session."); - - if (setup_tty(&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]); - - 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/backends/native/weston-launch.h b/src/backends/native/weston-launch.h deleted file mode 100644 index 1e716c5ac..000000000 --- a/src/backends/native/weston-launch.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * 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. - */ - -#ifndef _WESTON_LAUNCH_H_ -#define _WESTON_LAUNCH_H_ - -enum weston_launcher_message_type { - WESTON_LAUNCHER_REQUEST, - WESTON_LAUNCHER_EVENT, -}; - -enum weston_launcher_opcode { - WESTON_LAUNCHER_OPEN = (1 << 1 | WESTON_LAUNCHER_REQUEST), - WESTON_LAUNCHER_ACTIVATE_VT = (2 << 1 | WESTON_LAUNCHER_REQUEST), - WESTON_LAUNCHER_CONFIRM_VT_SWITCH = (3 << 1 | WESTON_LAUNCHER_REQUEST), -}; - -enum weston_launcher_server_opcode { - WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH = (1 << 1 | WESTON_LAUNCHER_EVENT), - WESTON_LAUNCHER_SERVER_VT_ENTER = (2 << 1 | WESTON_LAUNCHER_EVENT), -}; - -struct weston_launcher_message { - int opcode; -}; - -struct weston_launcher_open { - struct weston_launcher_message header; - int flags; - char path[0]; -}; - -struct weston_launcher_activate_vt { - struct weston_launcher_message header; - signed char vt; -}; - -struct weston_launcher_reply { - struct weston_launcher_message header; - int ret; -}; - -struct weston_launcher_event { - struct weston_launcher_message header; - int detail; /* unused, but makes sure replies and events are serialized the same */ -}; - -#endif diff --git a/src/mutter-wayland.desktop.in b/src/mutter-wayland.desktop.in index fb51a1734..850208942 100644 --- a/src/mutter-wayland.desktop.in +++ b/src/mutter-wayland.desktop.in @@ -1,7 +1,7 @@ [Desktop Entry] Type=Application _Name=Mutter (wayland compositor) -Exec=mutter-launch -- mutter --wayland --display-server +Exec=mutter --wayland --display-server NoDisplay=true # name of loadable control center module X-GNOME-WMSettingsModule=metacity diff --git a/src/org.freedesktop.login1.xml b/src/org.freedesktop.login1.xml new file mode 100644 index 000000000..924e397a4 --- /dev/null +++ b/src/org.freedesktop.login1.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +