From cc5b2ed2b8e3b57de86b74f4a9934df255b9d3a8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 22 Oct 2013 00:28:09 +0200 Subject: [PATCH] launcher: add logind backend 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 2661aad5..cfd4540a 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 8cd7ccaa..42244952 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 44545998..cbdbb3c1 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 4376345c..80e708df 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 05667fb4..07816a56 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 8b496c85..a1ff832e 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 9de52370..d5b2fc98 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 00000000..3766c582 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000..552395e7 --- /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 +#include +#include +#include + +#include "compositor.h" + +struct weston_logind; + +#if defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) + +#include + +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) */