[PATCH v2 4/4] launcher: add logind backend

David Herrmann dh.herrmann at gmail.com
Tue Oct 22 00:28:09 CEST 2013


Instead of connecting to weston-launch from launcher-util, we now try to
connect to logind first. If logind provides session-devices, we use them.
If not, we fall back to the old weston-launch facility.
---
 configure.ac           |  17 +-
 src/Makefile.am        |   8 +
 src/compositor-drm.c   |   3 +-
 src/compositor-fbdev.c |   2 +-
 src/compositor-rpi.c   |   2 +-
 src/launcher-util.c    |  46 ++-
 src/launcher-util.h    |   3 +-
 src/logind-util.c      | 913 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/logind-util.h      | 120 +++++++
 9 files changed, 1096 insertions(+), 18 deletions(-)
 create mode 100644 src/logind-util.c
 create mode 100644 src/logind-util.h

diff --git a/configure.ac b/configure.ac
index 2661aad..cfd4540 100644
--- a/configure.ac
+++ b/configure.ac
@@ -316,14 +316,21 @@ AC_ARG_ENABLE(resize-optimization,
 AS_IF([test "x$enable_resize_optimization" = "xyes"],
       [AC_DEFINE([USE_RESIZE_POOL], [1], [Use resize memory pool as a performance optimization])])
 
+PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login],
+                  [have_systemd_login=yes], [have_systemd_login=no])
+AS_IF([test "x$have_systemd_login" = "xyes"],
+      [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])])
+AM_CONDITIONAL(HAVE_SYSTEMD_LOGIN, test "x$have_systemd_login" = "xyes")
+
+PKG_CHECK_MODULES(SYSTEMD_LOGIN_209, [libsystemd-login >= 209],
+                  [have_systemd_login_209=yes], [have_systemd_login_209=no])
+AS_IF([test "x$have_systemd_login_209" = "xyes"],
+      [AC_DEFINE([HAVE_SYSTEMD_LOGIN_209], [1], [Have systemd-login >= 209])])
+
 AC_ARG_ENABLE(weston-launch, [  --enable-weston-launch],, enable_weston_launch=yes)
 AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch == xyes)
 if test x$enable_weston_launch == xyes; then
   PKG_CHECK_MODULES(WESTON_LAUNCH, [libdrm])
-  PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login],
-		    [have_systemd_login=yes], [have_systemd_login=no])
-  AS_IF([test "x$have_systemd_login" = "xyes"],
-	[AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])])
 
   AC_CHECK_LIB([pam], [pam_open_session], [have_pam=yes], [have_pam=no])
   if test x$have_pam == xno; then
@@ -495,7 +502,7 @@ AC_MSG_RESULT([
 	Build Tablet Shell		${enable_tablet_shell}
 
 	weston-launch utility		${enable_weston_launch}
-	weston-launch systemd support	${have_systemd_login}
+	systemd-login support		${have_systemd_login}
 
 	DRM Compositor			${enable_drm_compositor}
 	X11 Compositor			${enable_x11_compositor}
diff --git a/src/Makefile.am b/src/Makefile.am
index 8cd7cca..4224495 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,14 @@ weston_SOURCES +=				\
 	dbus.c
 weston_CFLAGS += $(DBUS_CFLAGS)
 weston_LDADD += $(DBUS_LIBS)
+
+if HAVE_SYSTEMD_LOGIN
+weston_SOURCES +=				\
+	logind-util.h				\
+	logind-util.c
+weston_CFLAGS += $(SYSTEMD_LOGIN_CFLAGS)
+weston_LDADD += $(SYSTEMD_LOGIN_LIBS)
+endif
 endif
 
 git-version.h : .FORCE
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 4454599..cbdbb3c 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -2623,7 +2623,8 @@ drm_compositor_create(struct wl_display *display,
 	}
 
 	/* Check if we run drm-backend using weston-launch */
-	ec->base.launcher = weston_launcher_connect(&ec->base, param->tty);
+	ec->base.launcher = weston_launcher_connect(&ec->base, param->tty,
+						    param->seat_id);
 	if (ec->base.launcher == NULL) {
 		weston_log("fatal: drm backend should be run "
 			   "using weston-launch binary or as root\n");
diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c
index 4376345..80e708d 100644
--- a/src/compositor-fbdev.c
+++ b/src/compositor-fbdev.c
@@ -894,7 +894,7 @@ fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[],
 	wl_signal_add(&compositor->base.session_signal,
 		      &compositor->session_listener);
 	compositor->base.launcher =
-		weston_launcher_connect(&compositor->base, param->tty);
+		weston_launcher_connect(&compositor->base, param->tty, "seat0");
 	if (!compositor->base.launcher) {
 		weston_log("fatal: fbdev backend should be run "
 			   "using weston-launch binary or as root\n");
diff --git a/src/compositor-rpi.c b/src/compositor-rpi.c
index 05667fb..07816a5 100644
--- a/src/compositor-rpi.c
+++ b/src/compositor-rpi.c
@@ -752,7 +752,7 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[],
 	wl_signal_add(&compositor->base.session_signal,
 		      &compositor ->session_listener);
 	compositor->base.launcher =
-		weston_launcher_connect(&compositor->base, param->tty);
+		weston_launcher_connect(&compositor->base, param->tty, "seat0");
 	if (!compositor->base.launcher) {
 		weston_log("Failed to initialize tty.\n");
 		goto out_udev;
diff --git a/src/launcher-util.c b/src/launcher-util.c
index 8b496c8..a1ff832 100644
--- a/src/launcher-util.c
+++ b/src/launcher-util.c
@@ -46,6 +46,7 @@
 
 #include "compositor.h"
 #include "launcher-util.h"
+#include "logind-util.h"
 #include "weston-launch.h"
 
 #define DRM_MAJOR 226
@@ -57,6 +58,7 @@
 union cmsg_data { unsigned char b[4]; int fd; };
 
 struct weston_launcher {
+	struct weston_logind *logind;
 	struct weston_compositor *compositor;
 	int fd;
 	struct wl_event_source *source;
@@ -104,6 +106,9 @@ weston_launcher_open(struct weston_launcher *launcher,
 	struct weston_launcher_open *message;
 	struct stat s;
 
+	if (launcher->logind)
+		return weston_logind_open(launcher->logind, path, flags);
+
 	if (launcher->fd == -1) {
 		fd = open(path, flags | O_CLOEXEC);
 		if (fd == -1)
@@ -176,6 +181,9 @@ weston_launcher_open(struct weston_launcher *launcher,
 void
 weston_launcher_close(struct weston_launcher *launcher, int fd)
 {
+	if (launcher->logind)
+		return weston_logind_close(launcher->logind, fd);
+
 	close(fd);
 }
 
@@ -184,6 +192,9 @@ weston_launcher_restore(struct weston_launcher *launcher)
 {
 	struct vt_mode mode = { 0 };
 
+	if (launcher->logind)
+		return weston_logind_restore(launcher->logind);
+
 	if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
 	    ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
 		weston_log("failed to restore kb mode: %m\n");
@@ -342,19 +353,25 @@ setup_tty(struct weston_launcher *launcher, int tty)
 int
 weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
 {
+	if (launcher->logind)
+		return weston_logind_activate_vt(launcher->logind, vt);
+
 	return ioctl(launcher->tty, VT_ACTIVATE, vt);
 }
 
 struct weston_launcher *
-weston_launcher_connect(struct weston_compositor *compositor, int tty)
+weston_launcher_connect(struct weston_compositor *compositor, int tty,
+			const char *seat_id)
 {
 	struct weston_launcher *launcher;
 	struct wl_event_loop *loop;
+	int r;
 
 	launcher = malloc(sizeof *launcher);
 	if (launcher == NULL)
 		return NULL;
 
+	launcher->logind = NULL;
 	launcher->compositor = compositor;
 	launcher->drm_fd = -1;
 	launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
@@ -369,14 +386,21 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty)
 			free(launcher);
 			return NULL;
 		}
-	} else if (geteuid() == 0) {
-		if (setup_tty(launcher, tty) == -1) {
-			free(launcher);
-			return NULL;
-		}
 	} else {
-		free(launcher);
-		return NULL;
+		r = weston_logind_connect(&launcher->logind, compositor,
+					  seat_id, tty);
+		if (r < 0) {
+			launcher->logind = NULL;
+			if (geteuid() == 0) {
+				if (setup_tty(launcher, tty) == -1) {
+					free(launcher);
+					return NULL;
+				}
+			} else {
+				free(launcher);
+				return NULL;
+			}
+		}
 	}
 
 	return launcher;
@@ -385,6 +409,8 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty)
 void
 weston_launcher_destroy(struct weston_launcher *launcher)
 {
+	if (launcher->logind)
+		weston_logind_destroy(launcher->logind);
 	if (launcher->fd != -1) {
 		close(launcher->fd);
 		wl_event_source_remove(launcher->source);
@@ -393,6 +419,8 @@ weston_launcher_destroy(struct weston_launcher *launcher)
 		wl_event_source_remove(launcher->vt_source);
 	}
 
-	close(launcher->tty);
+	if (launcher->tty >= 0)
+		close(launcher->tty);
+
 	free(launcher);
 }
diff --git a/src/launcher-util.h b/src/launcher-util.h
index 9de5237..d5b2fc9 100644
--- a/src/launcher-util.h
+++ b/src/launcher-util.h
@@ -30,7 +30,8 @@
 struct weston_launcher;
 
 struct weston_launcher *
-weston_launcher_connect(struct weston_compositor *compositor, int tty);
+weston_launcher_connect(struct weston_compositor *compositor, int tty,
+			const char *seat_id);
 
 void
 weston_launcher_destroy(struct weston_launcher *launcher);
diff --git a/src/logind-util.c b/src/logind-util.c
new file mode 100644
index 0000000..3766c58
--- /dev/null
+++ b/src/logind-util.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright © 2013 David Herrmann
+ *
+ * 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 <errno.h>
+#include <dbus.h>
+#include <fcntl.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/major.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/signalfd.h>
+#include <sys/stat.h>
+#include <systemd/sd-login.h>
+#include <unistd.h>
+
+#include "compositor.h"
+#include "dbus.h"
+#include "logind-util.h"
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE	0x4B51
+#endif
+
+struct weston_logind {
+	struct weston_compositor *compositor;
+	char *seat;
+	char *sid;
+	unsigned int vtnr;
+	int vt;
+	int kb_mode;
+	int sfd;
+	struct wl_event_source *sfd_source;
+
+	DBusConnection *dbus;
+	struct wl_event_source *dbus_ctx;
+	char *spath;
+	DBusPendingCall *pending_active;
+};
+
+static int
+weston_logind_take_device(struct weston_logind *wl, uint32_t major,
+			  uint32_t minor, bool *paused_out)
+{
+	DBusMessage *m, *reply;
+	bool b, paused;
+	int r, fd;
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.login1.Session",
+					 "TakeDevice");
+	if (!m)
+		return -ENOMEM;
+
+	b = dbus_message_append_args(m,
+				     DBUS_TYPE_UINT32, &major,
+				     DBUS_TYPE_UINT32, &minor,
+				     DBUS_TYPE_INVALID);
+	if (!b) {
+		r = -ENOMEM;
+		goto err_unref;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
+							  -1, NULL);
+	if (!reply) {
+		r = -ENODEV;
+		goto err_unref;
+	}
+
+	b = dbus_message_get_args(reply, NULL,
+				  DBUS_TYPE_UNIX_FD, &fd,
+				  DBUS_TYPE_BOOLEAN, &paused,
+				  DBUS_TYPE_INVALID);
+	if (!b) {
+		r = -ENODEV;
+		goto err_reply;
+	}
+
+	r = fd;
+	if (paused_out)
+		*paused_out = paused;
+
+err_reply:
+	dbus_message_unref(reply);
+err_unref:
+	dbus_message_unref(m);
+	return r;
+}
+
+static void
+weston_logind_release_device(struct weston_logind *wl, uint32_t major,
+			     uint32_t minor)
+{
+	DBusMessage *m;
+	bool b;
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.login1.Session",
+					 "ReleaseDevice");
+	if (m) {
+		b = dbus_message_append_args(m,
+					     DBUS_TYPE_UINT32, &major,
+					     DBUS_TYPE_UINT32, &minor,
+					     DBUS_TYPE_INVALID);
+		if (b)
+			dbus_connection_send(wl->dbus, m, NULL);
+		dbus_message_unref(m);
+	}
+}
+
+static void
+weston_logind_pause_device_complete(struct weston_logind *wl, uint32_t major,
+				    uint32_t minor)
+{
+	DBusMessage *m;
+	bool b;
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.login1.Session",
+					 "PauseDeviceComplete");
+	if (m) {
+		b = dbus_message_append_args(m,
+					     DBUS_TYPE_UINT32, &major,
+					     DBUS_TYPE_UINT32, &minor,
+					     DBUS_TYPE_INVALID);
+		if (b)
+			dbus_connection_send(wl->dbus, m, NULL);
+		dbus_message_unref(m);
+	}
+}
+
+WL_EXPORT int
+weston_logind_open(struct weston_logind *wl, const char *path,
+		   int flags)
+{
+	struct stat st;
+	int fl, r, fd;
+
+	r = stat(path, &st);
+	if (r < 0)
+		return -errno;
+	if (!S_ISCHR(st.st_mode))
+		return -ENODEV;
+
+	fd = weston_logind_take_device(wl, major(st.st_rdev),
+				       minor(st.st_rdev), NULL);
+	if (fd < 0)
+		return fd;
+
+	/* Compared to weston_launcher_open() we cannot specify the open-mode
+	 * directly. Instead, logind passes us an fd with sane default modes.
+	 * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
+	 * something else, we need to change it afterwards. We currently
+	 * only support dropping FD_CLOEXEC and setting O_NONBLOCK. Changing
+	 * access-modes is not possible so accept whatever logind passes us. */
+
+	fl = fcntl(fd, F_GETFL);
+	if (fl < 0) {
+		r = -errno;
+		goto err_close;
+	}
+
+	if (flags & O_NONBLOCK)
+		fl |= O_NONBLOCK;
+
+	r = fcntl(fd, F_SETFL, fl);
+	if (r < 0) {
+		r = -errno;
+		goto err_close;
+	}
+
+	fl = fcntl(fd, F_GETFD);
+	if (fl < 0) {
+		r = -errno;
+		goto err_close;
+	}
+
+	if (!(flags & O_CLOEXEC))
+		fl &= ~FD_CLOEXEC;
+
+	r = fcntl(fd, F_SETFD, fl);
+	if (r < 0) {
+		r = -errno;
+		goto err_close;
+	}
+
+	return fd;
+
+err_close:
+	close(fd);
+	weston_logind_release_device(wl, major(st.st_rdev),
+				     minor(st.st_rdev));
+	return r;
+}
+
+WL_EXPORT void
+weston_logind_close(struct weston_logind *wl, int fd)
+{
+	struct stat st;
+	int r;
+
+	r = fstat(fd, &st);
+	if (r < 0) {
+		weston_log("logind: cannot fstat fd: %m\n");
+		return;
+	}
+
+	if (!S_ISCHR(st.st_mode)) {
+		weston_log("logind: invalid device passed\n");
+		return;
+	}
+
+	weston_logind_release_device(wl, major(st.st_rdev),
+				     minor(st.st_rdev));
+}
+
+WL_EXPORT void
+weston_logind_restore(struct weston_logind *wl)
+{
+	struct vt_mode mode = { 0 };
+
+	ioctl(wl->vt, KDSETMODE, KD_TEXT);
+	ioctl(wl->vt, KDSKBMUTE, 0);
+	ioctl(wl->vt, KDSKBMODE, wl->kb_mode);
+	mode.mode = VT_AUTO;
+	ioctl(wl->vt, VT_SETMODE, &mode);
+}
+
+WL_EXPORT int
+weston_logind_activate_vt(struct weston_logind *wl, int vt)
+{
+	int r;
+
+	r = ioctl(wl->vt, VT_ACTIVATE, vt);
+	if (r < 0)
+		return -errno;
+
+	return 0;
+}
+
+static void
+weston_logind_set_active(struct weston_logind *wl, bool active)
+{
+	if (active)
+		wl->compositor->session_active = 1;
+	else
+		wl->compositor->session_active = 0;
+
+	wl_signal_emit(&wl->compositor->session_signal,
+		       wl->compositor);
+}
+
+static void
+get_active_cb(DBusPendingCall *pending, void *data)
+{
+	struct weston_logind *wl = data;
+	DBusMessage *m;
+	DBusMessageIter iter, sub;
+	int type;
+	bool b;
+
+	dbus_pending_call_unref(wl->pending_active);
+	wl->pending_active = NULL;
+
+	m = dbus_pending_call_steal_reply(pending);
+	if (!m)
+		return;
+
+	type = dbus_message_get_type(m);
+	if (type != DBUS_MESSAGE_TYPE_METHOD_RETURN)
+		goto err_unref;
+
+	if (!dbus_message_iter_init(m, &iter) ||
+	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		goto err_unref;
+
+	dbus_message_iter_recurse(&iter, &sub);
+
+	if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+		goto err_unref;
+
+	dbus_message_iter_get_basic(&sub, &b);
+	weston_logind_set_active(wl, b);
+
+err_unref:
+	dbus_message_unref(m);
+}
+
+static void
+weston_logind_get_active(struct weston_logind *wl)
+{
+	DBusPendingCall *pending;
+	DBusMessage *m;
+	bool b;
+	const char *iface, *name;
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.DBus.Properties",
+					 "Get");
+	if (!m)
+		return;
+
+	iface = "org.freedesktop.login1.Session";
+	name = "Active";
+	b = dbus_message_append_args(m,
+				     DBUS_TYPE_STRING, &iface,
+				     DBUS_TYPE_STRING, &name,
+				     DBUS_TYPE_INVALID);
+	if (!b)
+		goto err_unref;
+
+	b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
+	if (!b)
+		goto err_unref;
+
+	b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
+	if (!b) {
+		dbus_pending_call_cancel(pending);
+		dbus_pending_call_unref(pending);
+		goto err_unref;
+	}
+
+	if (wl->pending_active) {
+		dbus_pending_call_cancel(wl->pending_active);
+		dbus_pending_call_unref(wl->pending_active);
+	}
+	wl->pending_active = pending;
+	return;
+
+err_unref:
+	dbus_message_unref(m);
+}
+
+static void
+disconnected_dbus(struct weston_logind *wl)
+{
+	weston_log("logind: dbus connection lost, exiting..\n");
+	weston_logind_restore(wl);
+	exit(-1);
+}
+
+static void
+session_removed(struct weston_logind *wl, DBusMessage *m)
+{
+	const char *name, *obj;
+	bool r;
+
+	r = dbus_message_get_args(m, NULL,
+				  DBUS_TYPE_STRING, &name,
+				  DBUS_TYPE_OBJECT_PATH, &obj,
+				  DBUS_TYPE_INVALID);
+	if (!r) {
+		weston_log("logind: cannot parse SessionRemoved dbus signal\n");
+		return;
+	}
+
+	if (!strcmp(name, wl->sid)) {
+		weston_log("logind: our session got closed, exiting..\n");
+		weston_logind_restore(wl);
+		exit(-1);
+	}
+}
+
+static void
+property_changed(struct weston_logind *wl, DBusMessage *m)
+{
+	DBusMessageIter iter, sub, entry;
+	const char *interface, *name;
+	dbus_bool_t b;
+
+	if (!dbus_message_iter_init(m, &iter) ||
+	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		goto error;
+
+	dbus_message_iter_get_basic(&iter, &interface);
+
+	if (!dbus_message_iter_next(&iter) ||
+	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		goto error;
+
+	dbus_message_iter_recurse(&iter, &sub);
+
+	while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
+		dbus_message_iter_recurse(&sub, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			goto error;
+
+		dbus_message_iter_get_basic(&entry, &name);
+		if (!dbus_message_iter_next(&entry))
+			goto error;
+
+		if (!strcmp(name, "Active")) {
+			if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_BOOLEAN) {
+				dbus_message_iter_get_basic(&entry, &b);
+				weston_logind_set_active(wl, b);
+				return;
+			}
+		}
+
+		dbus_message_iter_next(&sub);
+	}
+
+	if (!dbus_message_iter_next(&iter) ||
+	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		goto error;
+
+	dbus_message_iter_recurse(&iter, &sub);
+
+	while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+		dbus_message_iter_get_basic(&sub, &name);
+
+		if (!strcmp(name, "Active")) {
+			weston_logind_get_active(wl);
+			return;
+		}
+
+		dbus_message_iter_next(&sub);
+	}
+
+	return;
+
+error:
+	weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
+}
+
+static void
+device_paused(struct weston_logind *wl, DBusMessage *m)
+{
+	bool r;
+	const char *type;
+	uint32_t major, minor;
+
+	r = dbus_message_get_args(m, NULL,
+				  DBUS_TYPE_UINT32, &major,
+				  DBUS_TYPE_UINT32, &minor,
+				  DBUS_TYPE_STRING, &type,
+				  DBUS_TYPE_INVALID);
+	if (!r) {
+		weston_log("logind: cannot parse PauseDevice dbus signal\n");
+		return;
+	}
+
+	/* "pause" means synchronous pausing. Acknowledge it unconditionally
+	 * as we support asynchronous device shutdowns, anyway.
+	 * "force" means asynchronous pausing. We ignore it as the following
+	 * session-deactivation will suffice as notification.
+	 * "gone" means the device is gone. We ignore it as we receive a
+	 * udev notification, anyway. */
+
+	if (!strcmp(type, "pause"))
+		weston_logind_pause_device_complete(wl, major, minor);
+}
+
+static void
+device_resumed(struct weston_logind *wl, DBusMessage *m)
+{
+	/*
+	 * DeviceResumed messages provide us a new file-descriptor for
+	 * resumed devices. For DRM devices it's the same as before, for evdev
+	 * devices it's a new open-file. As we reopen evdev devices, anyway,
+	 * there is no need for us to handle this event. If we ever optimize
+	 * our evdev code to support resuming devices, this event can be
+	 * parsed like this:
+	 * r = dbus_message_get_args(m, NULL,
+	 * 			  DBUS_TYPE_UINT32, &major,
+	 * 			  DBUS_TYPE_UINT32, &minor,
+	 * 			  DBUS_TYPE_UNIX_FD, &fd,
+	 * 			  DBUS_TYPE_INVALID);
+	 */
+}
+
+static DBusHandlerResult
+filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
+{
+	struct weston_logind *wl = data;
+
+	if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+		disconnected_dbus(wl);
+	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
+					  "SessionRemoved")) {
+		session_removed(wl, m);
+	} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
+					  "PropertiesChanged")) {
+		property_changed(wl, m);
+	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
+					  "PauseDevice")) {
+		device_paused(wl, m);
+	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
+					  "ResumeDevice")) {
+		device_resumed(wl, m);
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int
+weston_logind_setup_dbus(struct weston_logind *wl)
+{
+	bool b;
+	int r;
+
+	r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
+		     wl->sid);
+	if (r < 0)
+		return -ENOMEM;
+
+	b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
+	if (!b) {
+		weston_log("logind: cannot add dbus filter\n");
+		r = -ENOMEM;
+		goto err_spath;
+	}
+
+	r = weston_dbus_add_match_signal(wl->dbus,
+					 "org.freedesktop.login1",
+					 "org.freedesktop.login1.Manager",
+					 "SessionRemoved",
+					 "/org/freedesktop/login1");
+	if (r < 0) {
+		weston_log("logind: cannot add dbus match\n");
+		goto err_spath;
+	}
+
+	r = weston_dbus_add_match_signal(wl->dbus,
+					"org.freedesktop.login1",
+					"org.freedesktop.login1.Session",
+					"PauseDevice",
+					wl->spath);
+	if (r < 0) {
+		weston_log("logind: cannot add dbus match\n");
+		goto err_spath;
+	}
+
+	r = weston_dbus_add_match_signal(wl->dbus,
+					"org.freedesktop.login1",
+					"org.freedesktop.login1.Session",
+					"ResumeDevice",
+					wl->spath);
+	if (r < 0) {
+		weston_log("logind: cannot add dbus match\n");
+		goto err_spath;
+	}
+
+	r = weston_dbus_add_match_signal(wl->dbus,
+					"org.freedesktop.login1",
+					"org.freedesktop.DBus.Properties",
+					"PropertiesChanged",
+					wl->spath);
+	if (r < 0) {
+		weston_log("logind: cannot add dbus match\n");
+		goto err_spath;
+	}
+
+	return 0;
+
+err_spath:
+	/* don't remove any dbus-match as the connection is closed, anyway */
+	free(wl->spath);
+	return r;
+}
+
+static void
+weston_logind_destroy_dbus(struct weston_logind *wl)
+{
+	/* don't remove any dbus-match as the connection is closed, anyway */
+	free(wl->spath);
+}
+
+static int
+weston_logind_take_control(struct weston_logind *wl)
+{
+	DBusError err;
+	DBusMessage *m, *reply;
+	dbus_bool_t force;
+	bool b;
+	int r;
+
+	dbus_error_init(&err);
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.login1.Session",
+					 "TakeControl");
+	if (!m)
+		return -ENOMEM;
+
+	force = false;
+	b = dbus_message_append_args(m,
+				     DBUS_TYPE_BOOLEAN, &force,
+				     DBUS_TYPE_INVALID);
+	if (!b) {
+		r = -ENOMEM;
+		goto err_unref;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(wl->dbus,
+							  m, -1, &err);
+	if (!reply) {
+		if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
+			weston_log("logind: old systemd version detected\n");
+		else
+			weston_log("logind: cannot take control over session %s\n", wl->sid);
+
+		dbus_error_free(&err);
+		r = -EIO;
+		goto err_unref;
+	}
+
+	dbus_message_unref(reply);
+	dbus_message_unref(m);
+	return 0;
+
+err_unref:
+	dbus_message_unref(m);
+	return r;
+}
+
+static void
+weston_logind_release_control(struct weston_logind *wl)
+{
+	DBusMessage *m;
+
+	m = dbus_message_new_method_call("org.freedesktop.login1",
+					 wl->spath,
+					 "org.freedesktop.login1.Session",
+					 "ReleaseControl");
+	if (m) {
+		dbus_connection_send(wl->dbus, m, NULL);
+		dbus_message_unref(m);
+	}
+}
+
+static int
+signal_event(int fd, uint32_t mask, void *data)
+{
+	struct weston_logind *wl = data;
+	struct signalfd_siginfo sig;
+
+	if (read(fd, &sig, sizeof sig) != sizeof sig) {
+		weston_log("logind: cannot read signalfd: %m\n");
+		return 0;
+	}
+
+	switch (sig.ssi_signo) {
+	case SIGUSR1:
+		ioctl(wl->vt, VT_RELDISP, 1);
+		break;
+	case SIGUSR2:
+		ioctl(wl->vt, VT_RELDISP, VT_ACKACQ);
+		break;
+	}
+
+	return 0;
+}
+
+static int
+weston_logind_setup_vt(struct weston_logind *wl)
+{
+	struct stat st;
+	char buf[64];
+	struct vt_mode mode = { 0 };
+	int r;
+	sigset_t mask;
+	struct wl_event_loop *loop;
+
+	snprintf(buf, sizeof(buf), "/dev/tty%d", wl->vtnr);
+	buf[sizeof(buf) - 1] = 0;
+
+	wl->vt = open(buf, O_RDWR|O_CLOEXEC|O_NONBLOCK);
+
+	if (wl->vt < 0) {
+		r = -errno;
+		weston_log("logind: cannot open VT %s: %m\n", buf);
+		return r;
+	}
+
+	if (fstat(wl->vt, &st) == -1 ||
+	    major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) <= 0 ||
+	    minor(st.st_rdev) >= 64) {
+		r = -EINVAL;
+		weston_log("logind: TTY %s is no virtual terminal\n", buf);
+		goto err_close;
+	}
+
+	/*r = setsid();
+	if (r < 0 && errno != EPERM) {
+		r = -errno;
+		weston_log("logind: setsid() failed: %m\n");
+		goto err_close;
+	}
+
+	r = ioctl(wl->vt, TIOCSCTTY, 0);
+	if (r < 0)
+		weston_log("logind: VT %s already in use\n", buf);*/
+
+	if (ioctl(wl->vt, KDGKBMODE, &wl->kb_mode) < 0) {
+		weston_log("logind: cannot read keyboard mode on %s: %m\n",
+			   buf);
+		wl->kb_mode = K_UNICODE;
+	} else if (wl->kb_mode == K_OFF) {
+		wl->kb_mode = K_UNICODE;
+	}
+
+	if (ioctl(wl->vt, KDSKBMUTE, 1) < 0 &&
+	    ioctl(wl->vt, KDSKBMODE, K_OFF) < 0) {
+		r = -errno;
+		weston_log("logind: cannot set K_OFF KB-mode on %s: %m\n",
+			   buf);
+		goto err_close;
+	}
+
+	if (ioctl(wl->vt, KDSETMODE, KD_GRAPHICS) < 0) {
+		r = -errno;
+		weston_log("logind: cannot set KD_GRAPHICS mode on %s: %m\n",
+			   buf);
+		goto err_kbmode;
+	}
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGUSR1);
+	sigaddset(&mask, SIGUSR2);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+
+	wl->sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+	if (wl->sfd < 0) {
+		r = -errno;
+		weston_log("logind: cannot create signalfd: %m\n");
+		goto err_mode;
+	}
+
+	loop = wl_display_get_event_loop(wl->compositor->wl_display);
+	wl->sfd_source = wl_event_loop_add_fd(loop, wl->sfd,
+					      WL_EVENT_READABLE,
+					      signal_event, wl);
+	if (!wl->sfd_source) {
+		r = -errno;
+		weston_log("logind: cannot create signalfd source: %m\n");
+		goto err_sfd;
+	}
+
+	mode.mode = VT_PROCESS;
+	mode.relsig = SIGUSR1;
+	mode.acqsig = SIGUSR2;
+	if (ioctl(wl->vt, VT_SETMODE, &mode) < 0) {
+		r = -errno;
+		weston_log("logind: cannot take over VT: %m\n");
+		goto err_sfd_source;
+	}
+
+	weston_log("logind: using VT %s\n", buf);
+	return 0;
+
+err_sfd_source:
+	wl_event_source_remove(wl->sfd_source);
+err_sfd:
+	close(wl->sfd);
+err_mode:
+	ioctl(wl->vt, KDSETMODE, KD_TEXT);
+err_kbmode:
+	ioctl(wl->vt, KDSKBMUTE, 0);
+	ioctl(wl->vt, KDSKBMODE, wl->kb_mode);
+err_close:
+	close(wl->vt);
+	return r;
+}
+
+static void
+weston_logind_destroy_vt(struct weston_logind *wl)
+{
+	weston_logind_restore(wl);
+	wl_event_source_remove(wl->sfd_source);
+	close(wl->sfd);
+	close(wl->vt);
+}
+
+WL_EXPORT int
+weston_logind_connect(struct weston_logind **out,
+		      struct weston_compositor *compositor,
+		      const char *seat_id, int tty)
+{
+	struct weston_logind *wl;
+	struct wl_event_loop *loop;
+	char *t;
+	int r;
+
+	wl = calloc(1, sizeof(*wl));
+	if (!wl) {
+		r = -ENOMEM;
+		goto err_out;
+	}
+
+	wl->compositor = compositor;
+
+	wl->seat = strdup(seat_id);
+	if (!wl->seat) {
+		r = -ENOMEM;
+		goto err_wl;
+	}
+
+	r = sd_pid_get_session(getpid(), &wl->sid);
+	if (r < 0) {
+		weston_log("logind: not running in a systemd session\n");
+		goto err_seat;
+	}
+
+	t = NULL;
+	r = sd_session_get_seat(wl->sid, &t);
+	if (r < 0 || strcmp(seat_id, t)) {
+		weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
+			   seat_id, t);
+		free(t);
+		goto err_session;
+	}
+	free(t);
+
+	r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
+	if (r < 0) {
+		weston_log("logind: session not running on a VT\n");
+		goto err_session;
+	} else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
+		weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
+			   tty, wl->vtnr);
+		goto err_session;
+	}
+
+	loop = wl_display_get_event_loop(compositor->wl_display);
+	r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
+	if (r < 0) {
+		weston_log("logind: cannot connect to system dbus\n");
+		goto err_session;
+	}
+
+	r = weston_logind_setup_dbus(wl);
+	if (r < 0)
+		goto err_dbus;
+
+	r = weston_logind_take_control(wl);
+	if (r < 0)
+		goto err_dbus_cleanup;
+
+	r = weston_logind_setup_vt(wl);
+	if (r < 0)
+		goto err_control;
+
+	weston_log("logind: session control granted\n");
+	*out = wl;
+	return 0;
+
+err_control:
+	weston_logind_release_control(wl);
+err_dbus_cleanup:
+	weston_logind_destroy_dbus(wl);
+err_dbus:
+	weston_dbus_close(wl->dbus, wl->dbus_ctx);
+err_session:
+	free(wl->sid);
+err_seat:
+	free(wl->seat);
+err_wl:
+	free(wl);
+err_out:
+	weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
+	return r;
+}
+
+WL_EXPORT void
+weston_logind_destroy(struct weston_logind *wl)
+{
+	if (wl->pending_active) {
+		dbus_pending_call_cancel(wl->pending_active);
+		dbus_pending_call_unref(wl->pending_active);
+	}
+
+	weston_logind_destroy_vt(wl);
+	weston_logind_release_control(wl);
+	weston_logind_destroy_dbus(wl);
+	weston_dbus_close(wl->dbus, wl->dbus_ctx);
+	free(wl->sid);
+	free(wl->seat);
+	free(wl);
+}
diff --git a/src/logind-util.h b/src/logind-util.h
new file mode 100644
index 0000000..552395e
--- /dev/null
+++ b/src/logind-util.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2013 David Herrmann
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compositor.h"
+
+struct weston_logind;
+
+#if defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS)
+
+#include <systemd/sd-login.h>
+
+int
+weston_logind_open(struct weston_logind *wl, const char *path,
+		   int flags);
+
+void
+weston_logind_close(struct weston_logind *wl, int fd);
+
+void
+weston_logind_restore(struct weston_logind *wl);
+
+int
+weston_logind_activate_vt(struct weston_logind *wl, int vt);
+
+int
+weston_logind_connect(struct weston_logind **out,
+		      struct weston_compositor *compositor,
+		      const char *seat_id, int tty);
+
+void
+weston_logind_destroy(struct weston_logind *wl);
+
+static inline int
+weston_sd_session_get_vt(const char *sid, unsigned int *out)
+{
+#ifdef HAVE_SYSTEMD_LOGIN_209
+	return sd_session_get_vt(sid, out);
+#else
+	int r;
+	char *tty;
+
+	r = sd_session_get_tty(sid, &tty);
+	if (r < 0)
+		return r;
+
+	r = sscanf(tty, "tty%u", out);
+	free(tty);
+
+	if (r != 1)
+		return -EINVAL;
+
+	return 0;
+#endif
+}
+
+#else /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */
+
+static inline int
+weston_logind_open(struct weston_logind *wl, const char *path,
+		   int flags)
+{
+	return -ENOSYS;
+}
+
+static inline void
+weston_logind_close(struct weston_logind *wl, int fd)
+{
+}
+
+static inline void
+weston_logind_restore(struct weston_logind *wl)
+{
+}
+
+static inline int
+weston_logind_activate_vt(struct weston_logind *wl, int vt)
+{
+	return -ENOSYS;
+}
+
+static inline int
+weston_logind_connect(struct weston_logind **out,
+		      struct weston_compositor *compositor,
+		      const char *seat_id, int tty)
+{
+	return -ENOSYS;
+}
+
+static inline void
+weston_logind_destroy(struct weston_logind *wl)
+{
+}
+
+#endif /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */
-- 
1.8.4.1



More information about the wayland-devel mailing list