diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/login/logind-device.c | 4 | ||||
-rw-r--r-- | src/login/logind-device.h | 2 | ||||
-rw-r--r-- | src/login/logind-seat.c | 7 | ||||
-rw-r--r-- | src/login/logind-session-dbus.c | 126 | ||||
-rw-r--r-- | src/login/logind-session-device.c | 451 | ||||
-rw-r--r-- | src/login/logind-session-device.h | 59 | ||||
-rw-r--r-- | src/login/logind-session.c | 27 | ||||
-rw-r--r-- | src/login/logind-session.h | 2 | ||||
-rw-r--r-- | src/shared/missing.h | 13 |
10 files changed, 692 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 49c1a1d3c4..9b06c28833 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3768,6 +3768,8 @@ libsystemd_logind_core_la_SOURCES = \ src/login/logind-seat.h \ src/login/logind-session.c \ src/login/logind-session.h \ + src/login/logind-session-device.c \ + src/login/logind-session-device.h \ src/login/logind-user.c \ src/login/logind-user.h \ src/login/logind-inhibit.c \ diff --git a/src/login/logind-device.c b/src/login/logind-device.c index 2bcd6a10c5..95c2307baf 100644 --- a/src/login/logind-device.c +++ b/src/login/logind-device.c @@ -67,11 +67,15 @@ void device_free(Device *d) { void device_detach(Device *d) { Seat *s; + SessionDevice *sd; assert(d); if (!d->seat) return; + while ((sd = d->session_devices)) + session_device_free(sd); + s = d->seat; LIST_REMOVE(Device, devices, d->seat->devices, d); d->seat = NULL; diff --git a/src/login/logind-device.h b/src/login/logind-device.h index 315f0e66b0..fa6eda7e55 100644 --- a/src/login/logind-device.h +++ b/src/login/logind-device.h @@ -27,6 +27,7 @@ typedef struct Device Device; #include "util.h" #include "logind.h" #include "logind-seat.h" +#include "logind-session-device.h" struct Device { Manager *manager; @@ -38,6 +39,7 @@ struct Device { dual_timestamp timestamp; LIST_FIELDS(struct Device, devices); + LIST_HEAD(SessionDevice, session_devices); }; Device* device_new(Manager *m, const char *sysfs, bool master); diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 3dc529b2b9..f88738ab16 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -246,10 +246,15 @@ int seat_set_active(Seat *s, Session *session) { old_active = s->active; s->active = session; + if (old_active) + session_device_pause_all(old_active); + seat_apply_acls(s, old_active); - if (session && session->started) + if (session && session->started) { session_send_changed(session, "Active\0"); + session_device_resume_all(session); + } if (!session || session->started) seat_send_changed(s, "ActiveSession\0"); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 35bf4480cb..f793f99b77 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -24,6 +24,7 @@ #include "logind.h" #include "logind-session.h" +#include "logind-session-device.h" #include "dbus-common.h" #include "util.h" @@ -44,6 +45,30 @@ " <arg name=\"force\" type=\"b\"/>\n" \ " </method>\n" \ " <method name=\"ReleaseControl\"/>\n" \ + " <method name=\"TakeDevice\">\n" \ + " <arg name=\"major\" type=\"u\" direction=\"in\"/>\n" \ + " <arg name=\"minor\" type=\"u\" direction=\"in\"/>\n" \ + " <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \ + " <arg name=\"paused\" type=\"b\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"ReleaseDevice\">\n" \ + " <arg name=\"major\" type=\"u\"/>\n" \ + " <arg name=\"minor\" type=\"u\"/>\n" \ + " </method>\n" \ + " <method name=\"PauseDeviceComplete\">\n" \ + " <arg name=\"major\" type=\"u\"/>\n" \ + " <arg name=\"minor\" type=\"u\"/>\n" \ + " </method>\n" \ + " <signal name=\"PauseDevice\">\n" \ + " <arg name=\"major\" type=\"u\"/>\n" \ + " <arg name=\"minor\" type=\"u\"/>\n" \ + " <arg name=\"type\" type=\"s\"/>\n" \ + " </signal>\n" \ + " <signal name=\"ResumeDevice\">\n" \ + " <arg name=\"major\" type=\"u\"/>\n" \ + " <arg name=\"minor\" type=\"u\"/>\n" \ + " <arg name=\"fd\" type=\"h\"/>\n" \ + " </signal>\n" \ " <signal name=\"Lock\"/>\n" \ " <signal name=\"Unlock\"/>\n" \ " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \ @@ -408,6 +433,107 @@ static DBusHandlerResult session_message_dispatch( if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeDevice")) { + SessionDevice *sd; + bool b; + dbus_bool_t paused; + uint32_t major, minor; + dev_t dev; + + if (!session_is_controller(s, bus_message_get_sender_with_fallback(message))) + return bus_send_error_reply(connection, message, NULL, -EPERM); + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + dev = makedev(major, minor); + assert_cc(sizeof(unsigned long) >= sizeof(dev_t)); + + sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)dev)); + if (sd) { + /* We don't allow retrieving a device multiple times. + * The related ReleaseDevice call is not ref-counted. + * The caller should use dup() if it requires more than + * one fd (it would be functionally equivalent). */ + return bus_send_error_reply(connection, message, &error, -EBUSY); + } + + r = session_device_new(s, dev, &sd); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) { + session_device_free(sd); + goto oom; + } + + paused = !sd->active; + b = dbus_message_append_args( + reply, + DBUS_TYPE_UNIX_FD, &sd->fd, + DBUS_TYPE_BOOLEAN, &paused, + DBUS_TYPE_INVALID); + if (!b) { + session_device_free(sd); + return bus_send_error_reply(connection, message, NULL, -ENOMEM); + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseDevice")) { + SessionDevice *sd; + uint32_t major, minor; + + if (!session_is_controller(s, bus_message_get_sender_with_fallback(message))) + return bus_send_error_reply(connection, message, NULL, -EPERM); + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor))); + if (!sd) + return bus_send_error_reply(connection, message, NULL, -ENODEV); + + session_device_free(sd); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "PauseDeviceComplete")) { + SessionDevice *sd; + uint32_t major, minor; + + if (!session_is_controller(s, bus_message_get_sender_with_fallback(message))) + return bus_send_error_reply(connection, message, NULL, -EPERM); + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor))); + if (!sd) + return bus_send_error_reply(connection, message, NULL, -ENODEV); + + session_device_complete_pause(sd); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else { const BusBoundProperties bps[] = { { "org.freedesktop.login1.Session", bus_login_session_properties, s }, diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c new file mode 100644 index 0000000000..80fd364139 --- /dev/null +++ b/src/login/logind-session-device.c @@ -0,0 +1,451 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <fcntl.h> +#include <libudev.h> +#include <linux/input.h> +#include <linux/ioctl.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "dbus-common.h" +#include "logind-session-device.h" +#include "util.h" +#include "missing.h" + +enum SessionDeviceNotifications { + SESSION_DEVICE_RESUME, + SESSION_DEVICE_TRY_PAUSE, + SESSION_DEVICE_PAUSE, + SESSION_DEVICE_RELEASE, +}; + +static void session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) { + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; + _cleanup_free_ char *path = NULL; + const char *t = NULL; + + assert(sd); + + if (!sd->session->controller) + return; + + path = session_bus_path(sd->session); + if (!path) + return; + + m = dbus_message_new_signal(path, + "org.freedesktop.login1.Session", + (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice"); + if (!m) + return; + + if (!dbus_message_set_destination(m, sd->session->controller)) + return; + + switch (type) { + case SESSION_DEVICE_RESUME: + if (!dbus_message_append_args(m, + DBUS_TYPE_UINT32, major(sd->dev), + DBUS_TYPE_UINT32, minor(sd->dev), + DBUS_TYPE_UNIX_FD, &sd->fd, + DBUS_TYPE_INVALID)) + return; + break; + case SESSION_DEVICE_TRY_PAUSE: + t = "pause"; + break; + case SESSION_DEVICE_PAUSE: + t = "force"; + break; + case SESSION_DEVICE_RELEASE: + t = "gone"; + break; + default: + return; + } + + if (t && !dbus_message_append_args(m, + DBUS_TYPE_UINT32, major(sd->dev), + DBUS_TYPE_UINT32, minor(sd->dev), + DBUS_TYPE_STRING, &t, + DBUS_TYPE_INVALID)) + return; + + dbus_connection_send(sd->session->manager->bus, m, NULL); +} + +static int sd_eviocrevoke(int fd) { + static bool warned; + int r; + + assert(fd >= 0); + + r = ioctl(fd, EVIOCREVOKE, 1); + if (r < 0) { + r = -errno; + if (r == -EINVAL && !warned) { + warned = true; + log_warning("kernel does not support evdev-revocation"); + } + } + + return 0; +} + +static int sd_drmsetmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int sd_drmdropmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int session_device_open(SessionDevice *sd, bool active) { + int fd; + + assert(sd->type != DEVICE_TYPE_UNKNOWN); + + /* open device and try to get an udev_device from it */ + fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (fd < 0) + return -errno; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + if (active) + sd_drmsetmaster(fd); + else { + /* DRM-Master is granted to the first user who opens a + * device automatically (ughh, racy!). Hence, we just + * drop DRM-Master in case we were the first. */ + sd_drmdropmaster(fd); + } + break; + case DEVICE_TYPE_EVDEV: + if (!active) + sd_eviocrevoke(fd); + break; + case DEVICE_TYPE_FBDEV: + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + return fd; +} + +static int session_device_start(SessionDevice *sd) { + int r; + + assert(sd); + assert(session_is_active(sd->session)); + + if (sd->active) + return 0; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* Device is kept open. Simply call drmSetMaster() and hope + * there is no-one else. In case it fails, we keep the device + * paused. Maybe at some point we have a drmStealMaster(). */ + r = sd_drmsetmaster(sd->fd); + if (r < 0) + return r; + break; + case DEVICE_TYPE_EVDEV: + /* Evdev devices are revoked while inactive. Reopen it and we + * are fine. */ + r = session_device_open(sd, true); + if (r < 0) + return r; + close_nointr_nofail(sd->fd); + sd->fd = r; + break; + case DEVICE_TYPE_FBDEV: + /* fbdev devices have no way to synchronize access. Moreover, + * they mostly operate through mmaps() without any pageflips + * and modesetting, so there is no way for us to prevent access + * but tear down mmaps. + * That would be quite expensive to do on a per-fd context. So + * ignore legcy fbdev and let its users feel the pain they asked + * for when deciding for fbdev. */ + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + sd->active = true; + return 0; +} + +static void session_device_stop(SessionDevice *sd) { + assert(sd); + + if (!sd->active) + return; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* On DRM devices we simply drop DRM-Master but keep it open. + * This allows the user to keep resources allocated. The + * CAP_SYS_ADMIN restriction to DRM-Master prevents users from + * circumventing this. */ + sd_drmdropmaster(sd->fd); + break; + case DEVICE_TYPE_EVDEV: + /* Revoke access on evdev file-descriptors during deactivation. + * This will basically prevent any operations on the fd and + * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN + * protection this way. */ + sd_eviocrevoke(sd->fd); + break; + case DEVICE_TYPE_FBDEV: + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices without synchronization */ + break; + } + + sd->active = false; +} + +static DeviceType detect_device_type(struct udev_device *dev) { + const char *sysname, *subsystem; + DeviceType type; + + sysname = udev_device_get_sysname(dev); + subsystem = udev_device_get_subsystem(dev); + type = DEVICE_TYPE_UNKNOWN; + + if (streq_ptr(subsystem, "graphics")) { + if (!streq(sysname, "fbcon") && startswith(sysname, "fb")) + type = DEVICE_TYPE_FBDEV; + } else if (streq_ptr(subsystem, "drm")) { + if (startswith(sysname, "card")) + type = DEVICE_TYPE_DRM; + } else if (streq_ptr(subsystem, "input")) { + if (startswith(sysname, "event")) + type = DEVICE_TYPE_EVDEV; + } + + return type; +} + +static int session_device_verify(SessionDevice *sd) { + struct udev_device *dev, *p = NULL; + const char *sp, *node; + int r; + + dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev); + if (!dev) + return -ENODEV; + + sp = udev_device_get_syspath(dev); + node = udev_device_get_devnode(dev); + if (!node) { + r = -EINVAL; + goto err_dev; + } + + /* detect device type so we can find the correct sysfs parent */ + sd->type = detect_device_type(dev); + if (sd->type == DEVICE_TYPE_UNKNOWN) { + r = -ENODEV; + goto err_dev; + } else if (sd->type == DEVICE_TYPE_EVDEV) { + /* for evdev devices we need the parent node as device */ + p = dev; + dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL); + if (!dev) { + r = -ENODEV; + goto err_dev; + } + sp = udev_device_get_syspath(dev); + } else if (sd->type != DEVICE_TYPE_FBDEV && + sd->type != DEVICE_TYPE_DRM) { + /* Prevent opening unsupported devices. Especially devices of + * subsystem "input" must be opened via the evdev node as + * we require EVIOCREVOKE. */ + r = -ENODEV; + goto err_dev; + } + + /* search for an existing seat device and return it if available */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + /* The caller might have gotten the udev event before we were + * able to process it. Hence, fake the "add" event and let the + * logind-manager handle the new device. */ + r = manager_process_seat_device(sd->session->manager, dev); + if (r < 0) + goto err_dev; + + /* if it's still not available, then the device is invalid */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + r = -ENODEV; + goto err_dev; + } + } + + if (sd->device->seat != sd->session->seat) { + r = -EPERM; + goto err_dev; + } + + sd->node = strdup(node); + if (!sd->node) { + r = -ENOMEM; + goto err_dev; + } + + r = 0; +err_dev: + udev_device_unref(p ? : dev); + return r; +} + +int session_device_new(Session *s, dev_t dev, SessionDevice **out) { + SessionDevice *sd; + int r; + + assert(s); + assert(out); + + if (!s->seat) + return -EPERM; + + sd = new0(SessionDevice, 1); + if (!sd) + return -ENOMEM; + + sd->session = s; + sd->dev = dev; + sd->fd = -1; + sd->type = DEVICE_TYPE_UNKNOWN; + + r = session_device_verify(sd); + if (r < 0) + goto error; + + assert_cc(sizeof(unsigned long) >= sizeof(dev_t)); + + r = hashmap_put(s->devices, ULONG_TO_PTR((unsigned long)sd->dev), sd); + if (r < 0) { + r = -ENOMEM; + goto error; + } + + /* Open the device for the first time. We need a valid fd to pass back + * to the caller. If the session is not active, this _might_ immediately + * revoke access and thus invalidate the fd. But this is still needed + * to pass a valid fd back. */ + sd->active = session_is_active(s); + sd->fd = session_device_open(sd, sd->active); + if (sd->fd < 0) + goto error; + + LIST_PREPEND(SessionDevice, sd_by_device, sd->device->session_devices, sd); + + *out = sd; + return 0; + +error: + hashmap_remove(s->devices, ULONG_TO_PTR((unsigned long)sd->dev)); + free(sd->node); + free(sd); + return r; +} + +void session_device_free(SessionDevice *sd) { + assert(sd); + + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_RELEASE); + close_nointr_nofail(sd->fd); + + LIST_REMOVE(SessionDevice, sd_by_device, sd->device->session_devices, sd); + + hashmap_remove(sd->session->devices, ULONG_TO_PTR((unsigned long)sd->dev)); + + free(sd->node); + free(sd); +} + +void session_device_complete_pause(SessionDevice *sd) { + if (!sd->active) + return; + + session_device_stop(sd); +} + +void session_device_resume_all(Session *s) { + SessionDevice *sd; + Iterator i; + int r; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (!sd->active) { + r = session_device_start(sd); + if (!r) + session_device_notify(sd, SESSION_DEVICE_RESUME); + } + } +} + +void session_device_pause_all(Session *s) { + SessionDevice *sd; + Iterator i; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (sd->active) { + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_PAUSE); + } + } +} diff --git a/src/login/logind-session-device.h b/src/login/logind-session-device.h new file mode 100644 index 0000000000..511fce0e61 --- /dev/null +++ b/src/login/logind-session-device.h @@ -0,0 +1,59 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef enum DeviceType DeviceType; +typedef struct SessionDevice SessionDevice; + +#include "list.h" +#include "util.h" +#include "logind.h" +#include "logind-device.h" +#include "logind-seat.h" +#include "logind-session.h" + +enum DeviceType { + DEVICE_TYPE_UNKNOWN, + DEVICE_TYPE_FBDEV, + DEVICE_TYPE_DRM, + DEVICE_TYPE_EVDEV, +}; + +struct SessionDevice { + Session *session; + Device *device; + + dev_t dev; + char *node; + int fd; + bool active; + DeviceType type; + + LIST_FIELDS(struct SessionDevice, sd_by_device); +}; + +int session_device_new(Session *s, dev_t dev, SessionDevice **out); +void session_device_free(SessionDevice *sd); +void session_device_complete_pause(SessionDevice *sd); + +void session_device_resume_all(Session *s); +void session_device_pause_all(Session *s); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index f856127eae..fcc1901ed6 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -53,9 +53,17 @@ Session* session_new(Manager *m, const char *id) { return NULL; } + s->devices = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!s->devices) { + free(s->state_file); + free(s); + return NULL; + } + s->id = path_get_file_name(s->state_file); if (hashmap_put(m->sessions, s->id, s) < 0) { + hashmap_free(s->devices); free(s->state_file); free(s); return NULL; @@ -68,6 +76,8 @@ Session* session_new(Manager *m, const char *id) { } void session_free(Session *s) { + SessionDevice *sd; + assert(s); if (s->in_gc_queue) @@ -75,6 +85,11 @@ void session_free(Session *s) { session_drop_controller(s); + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + hashmap_free(s->devices); + if (s->user) { LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); @@ -612,6 +627,7 @@ int session_stop(Session *s) { int session_finalize(Session *s) { int r = 0; + SessionDevice *sd; assert(s); @@ -627,6 +643,10 @@ int session_finalize(Session *s) { "MESSAGE=Removed session %s.", s->id, NULL); + /* Kill session devices */ + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + /* Remove X11 symlink */ session_unlink_x11_socket(s); @@ -950,6 +970,8 @@ int session_set_controller(Session *s, const char *sender, bool force) { } void session_drop_controller(Session *s) { + SessionDevice *sd; + assert(s); if (!s->controller) @@ -958,6 +980,11 @@ void session_drop_controller(Session *s) { manager_drop_busname(s->manager, s->controller); free(s->controller); s->controller = NULL; + + /* Drop all devices as they're now unused. Do that after the controller + * is released to avoid sending out useles dbus signals. */ + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); } static const char* const session_state_table[_SESSION_STATE_MAX] = { diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 839fb230dc..f175a8995e 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -28,6 +28,7 @@ typedef enum KillWho KillWho; #include "util.h" #include "logind.h" #include "logind-seat.h" +#include "logind-session-device.h" #include "logind-user.h" #include "login-shared.h" @@ -107,6 +108,7 @@ struct Session { DBusMessage *create_message; char *controller; + Hashmap *devices; LIST_FIELDS(Session, sessions_by_user); LIST_FIELDS(Session, sessions_by_seat); diff --git a/src/shared/missing.h b/src/shared/missing.h index d1ca135c55..6c038d9f08 100644 --- a/src/shared/missing.h +++ b/src/shared/missing.h @@ -29,6 +29,7 @@ #include <stdlib.h> #include <unistd.h> #include <linux/oom.h> +#include <linux/input.h> #ifdef HAVE_AUDIT #include <libaudit.h> @@ -310,3 +311,15 @@ static inline int name_to_handle_at(int fd, const char *name, struct file_handle #ifndef SO_REUSEPORT #define SO_REUSEPORT 15 #endif + +#ifndef EVIOCREVOKE +#define EVIOCREVOKE _IOW('E', 0x91, int) +#endif + +#ifndef DRM_IOCTL_SET_MASTER +#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +#endif + +#ifndef DRM_IOCTL_DROP_MASTER +#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +#endif |