We're refactoring this to have multiple launcher "implementations". --- Makefile.am | 4 +- src/launcher-logind.c | 940 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/launcher-logind.h | 123 +++++++ src/launcher-util.c | 2 +- src/logind-util.c | 940 -------------------------------------------------- src/logind-util.h | 123 ------- 6 files changed, 1066 insertions(+), 1066 deletions(-) create mode 100644 src/launcher-logind.c create mode 100644 src/launcher-logind.h delete mode 100644 src/logind-util.c delete mode 100644 src/logind-util.h
diff --git a/Makefile.am b/Makefile.am index 62719c9..79a442c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -138,8 +138,8 @@ if HAVE_SYSTEMD_LOGIN libsession_helper_la_SOURCES += \ src/dbus.h \ src/dbus.c \ - src/logind-util.h \ - src/logind-util.c + src/launcher-logind.h \ + src/launcher-logind.c libsession_helper_la_CFLAGS += $(SYSTEMD_LOGIN_CFLAGS) $(DBUS_CFLAGS) libsession_helper_la_LIBADD += $(SYSTEMD_LOGIN_LIBS) $(DBUS_LIBS) endif diff --git a/src/launcher-logind.c b/src/launcher-logind.c new file mode 100644 index 0000000..4fab9a4 --- /dev/null +++ b/src/launcher-logind.c @@ -0,0 +1,940 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include <errno.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 "launcher-logind.h" + +#define DRM_MAJOR 226 + +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +struct weston_logind { + struct weston_compositor *compositor; + bool sync_drm; + 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; + int r, fd; + dbus_bool_t paused; + + 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 -1; + if (!S_ISCHR(st.st_mode)) { + errno = ENODEV; + return -1; + } + + 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 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; + } + return fd; + +err_close: + close(fd); + weston_logind_release_device(wl, major(st.st_rdev), + minor(st.st_rdev)); + errno = -r; + return -1; +} + +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 -1; + + return 0; +} + +static void +weston_logind_set_active(struct weston_logind *wl, bool active) +{ + if (!wl->compositor->session_active == !active) + return; + + wl->compositor->session_active = active; + + wl_signal_emit(&wl->compositor->session_signal, + wl->compositor); +} + +static void +parse_active(struct weston_logind *wl, DBusMessage *m, DBusMessageIter *iter) +{ + DBusMessageIter sub; + dbus_bool_t b; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + return; + + dbus_message_iter_recurse(iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) + return; + + dbus_message_iter_get_basic(&sub, &b); + + /* If the backend requested DRM master-device synchronization, we only + * wake-up the compositor once the master-device is up and running. For + * other backends, we immediately forward the Active-change event. */ + if (!wl->sync_drm || !b) + weston_logind_set_active(wl, b); +} + +static void +get_active_cb(DBusPendingCall *pending, void *data) +{ + struct weston_logind *wl = data; + DBusMessageIter iter; + DBusMessage *m; + int type; + + 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 && + dbus_message_iter_init(m, &iter)) + parse_active(wl, m, &iter); + + 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; + + 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")) { + parse_active(wl, m, &entry); + 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. + * "gone" means the device is gone. We handle it the same as "force" as + * a following udev event will be caught, too. + * + * If it's our main DRM device, tell the compositor to go asleep. */ + + if (!strcmp(type, "pause")) + weston_logind_pause_device_complete(wl, major, minor); + + if (wl->sync_drm && major == DRM_MAJOR) + weston_logind_set_active(wl, false); +} + +static void +device_resumed(struct weston_logind *wl, DBusMessage *m) +{ + bool r; + uint32_t major; + + r = dbus_message_get_args(m, NULL, + DBUS_TYPE_UINT32, &major, + /*DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_UNIX_FD, &fd,*/ + DBUS_TYPE_INVALID); + if (!r) { + weston_log("logind: cannot parse ResumeDevice dbus signal\n"); + return; + } + + /* 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 for evdev. For DRM, we + * notify the compositor to wake up. */ + + if (wl->sync_drm && major == DRM_MAJOR) + weston_logind_set_active(wl, true); +} + +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; + } + + if (sig.ssi_signo == (unsigned int)SIGRTMIN) + ioctl(wl->vt, VT_RELDISP, 1); + else if (sig.ssi_signo == (unsigned int)SIGRTMIN + 1) + ioctl(wl->vt, VT_RELDISP, VT_ACKACQ); + + 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%u", 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; + } + + /* + * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used + * as VT-acquire signal. Note that SIGRT* must be tested on runtime, as + * their exact values are not known at compile-time. POSIX requires 32 + * of them to be available, though. + */ + if (SIGRTMIN + 1 > SIGRTMAX) { + weston_log("logind: not enough RT signals available: %u-%u\n", + SIGRTMIN, SIGRTMAX); + return -EINVAL; + } + + sigemptyset(&mask); + sigaddset(&mask, SIGRTMIN); + sigaddset(&mask, SIGRTMIN + 1); + 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 = SIGRTMIN; + mode.acqsig = SIGRTMIN + 1; + 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, bool sync_drm) +{ + struct weston_logind *wl; + struct wl_event_loop *loop; + char *t; + int r; + + wl = zalloc(sizeof(*wl)); + if (wl == NULL) { + r = -ENOMEM; + goto err_out; + } + + wl->compositor = compositor; + wl->sync_drm = sync_drm; + + 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) { + weston_log("logind: failed to get session seat\n"); + free(t); + goto err_session; + } else if (strcmp(seat_id, t)) { + weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n", + seat_id, t); + r = -EINVAL; + 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); + r = -EINVAL; + 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); + errno = -r; + return -1; +} + +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/launcher-logind.h b/src/launcher-logind.h new file mode 100644 index 0000000..1bc4961 --- /dev/null +++ b/src/launcher-logind.h @@ -0,0 +1,123 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * 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, bool sync_drm); + +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, bool sync_drm) +{ + return -ENOSYS; +} + +static inline void +weston_logind_destroy(struct weston_logind *wl) +{ +} + +#endif /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ diff --git a/src/launcher-util.c b/src/launcher-util.c index 6205e5f..dc02dff 100644 --- a/src/launcher-util.c +++ b/src/launcher-util.c @@ -45,7 +45,7 @@ #include "compositor.h" #include "launcher-util.h" -#include "logind-util.h" +#include "launcher-logind.h" #include "weston-launch.h" #define DRM_MAJOR 226 diff --git a/src/logind-util.c b/src/logind-util.c deleted file mode 100644 index 006a1ae..0000000 --- a/src/logind-util.c +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Copyright © 2013 David Herrmann - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include <errno.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" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -struct weston_logind { - struct weston_compositor *compositor; - bool sync_drm; - 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; - int r, fd; - dbus_bool_t paused; - - 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 -1; - if (!S_ISCHR(st.st_mode)) { - errno = ENODEV; - return -1; - } - - 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 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; - } - return fd; - -err_close: - close(fd); - weston_logind_release_device(wl, major(st.st_rdev), - minor(st.st_rdev)); - errno = -r; - return -1; -} - -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 -1; - - return 0; -} - -static void -weston_logind_set_active(struct weston_logind *wl, bool active) -{ - if (!wl->compositor->session_active == !active) - return; - - wl->compositor->session_active = active; - - wl_signal_emit(&wl->compositor->session_signal, - wl->compositor); -} - -static void -parse_active(struct weston_logind *wl, DBusMessage *m, DBusMessageIter *iter) -{ - DBusMessageIter sub; - dbus_bool_t b; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) - return; - - dbus_message_iter_recurse(iter, &sub); - - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) - return; - - dbus_message_iter_get_basic(&sub, &b); - - /* If the backend requested DRM master-device synchronization, we only - * wake-up the compositor once the master-device is up and running. For - * other backends, we immediately forward the Active-change event. */ - if (!wl->sync_drm || !b) - weston_logind_set_active(wl, b); -} - -static void -get_active_cb(DBusPendingCall *pending, void *data) -{ - struct weston_logind *wl = data; - DBusMessageIter iter; - DBusMessage *m; - int type; - - 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 && - dbus_message_iter_init(m, &iter)) - parse_active(wl, m, &iter); - - 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; - - 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")) { - parse_active(wl, m, &entry); - 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. - * "gone" means the device is gone. We handle it the same as "force" as - * a following udev event will be caught, too. - * - * If it's our main DRM device, tell the compositor to go asleep. */ - - if (!strcmp(type, "pause")) - weston_logind_pause_device_complete(wl, major, minor); - - if (wl->sync_drm && major == DRM_MAJOR) - weston_logind_set_active(wl, false); -} - -static void -device_resumed(struct weston_logind *wl, DBusMessage *m) -{ - bool r; - uint32_t major; - - r = dbus_message_get_args(m, NULL, - DBUS_TYPE_UINT32, &major, - /*DBUS_TYPE_UINT32, &minor, - DBUS_TYPE_UNIX_FD, &fd,*/ - DBUS_TYPE_INVALID); - if (!r) { - weston_log("logind: cannot parse ResumeDevice dbus signal\n"); - return; - } - - /* 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 for evdev. For DRM, we - * notify the compositor to wake up. */ - - if (wl->sync_drm && major == DRM_MAJOR) - weston_logind_set_active(wl, true); -} - -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; - } - - if (sig.ssi_signo == (unsigned int)SIGRTMIN) - ioctl(wl->vt, VT_RELDISP, 1); - else if (sig.ssi_signo == (unsigned int)SIGRTMIN + 1) - ioctl(wl->vt, VT_RELDISP, VT_ACKACQ); - - 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%u", 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; - } - - /* - * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used - * as VT-acquire signal. Note that SIGRT* must be tested on runtime, as - * their exact values are not known at compile-time. POSIX requires 32 - * of them to be available, though. - */ - if (SIGRTMIN + 1 > SIGRTMAX) { - weston_log("logind: not enough RT signals available: %u-%u\n", - SIGRTMIN, SIGRTMAX); - return -EINVAL; - } - - sigemptyset(&mask); - sigaddset(&mask, SIGRTMIN); - sigaddset(&mask, SIGRTMIN + 1); - 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 = SIGRTMIN; - mode.acqsig = SIGRTMIN + 1; - 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, bool sync_drm) -{ - struct weston_logind *wl; - struct wl_event_loop *loop; - char *t; - int r; - - wl = zalloc(sizeof(*wl)); - if (wl == NULL) { - r = -ENOMEM; - goto err_out; - } - - wl->compositor = compositor; - wl->sync_drm = sync_drm; - - 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) { - weston_log("logind: failed to get session seat\n"); - free(t); - goto err_session; - } else if (strcmp(seat_id, t)) { - weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n", - seat_id, t); - r = -EINVAL; - 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); - r = -EINVAL; - 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); - errno = -r; - return -1; -} - -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 deleted file mode 100644 index 1bc4961..0000000 --- a/src/logind-util.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © 2013 David Herrmann - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * 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, bool sync_drm); - -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, bool sync_drm) -{ - return -ENOSYS; -} - -static inline void -weston_logind_destroy(struct weston_logind *wl) -{ -} - -#endif /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ -- 2.4.3 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel