1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 David Herrmann
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <linux/input.h>
26 #include <linux/ioctl.h>
28 #include <sys/ioctl.h>
30 #include <sys/types.h>
33 #include "dbus-common.h"
34 #include "logind-session-device.h"
38 enum SessionDeviceNotifications {
39 SESSION_DEVICE_RESUME,
40 SESSION_DEVICE_TRY_PAUSE,
42 SESSION_DEVICE_RELEASE,
45 static void session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
46 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
47 _cleanup_free_ char *path = NULL;
49 uint32_t major, minor;
53 major = major(sd->dev);
54 minor = minor(sd->dev);
56 if (!sd->session->controller)
59 path = session_bus_path(sd->session);
63 m = dbus_message_new_signal(path,
64 "org.freedesktop.login1.Session",
65 (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
69 if (!dbus_message_set_destination(m, sd->session->controller))
73 case SESSION_DEVICE_RESUME:
74 if (!dbus_message_append_args(m,
75 DBUS_TYPE_UINT32, &major,
76 DBUS_TYPE_UINT32, &minor,
77 DBUS_TYPE_UNIX_FD, &sd->fd,
81 case SESSION_DEVICE_TRY_PAUSE:
84 case SESSION_DEVICE_PAUSE:
87 case SESSION_DEVICE_RELEASE:
94 if (t && !dbus_message_append_args(m,
95 DBUS_TYPE_UINT32, &major,
96 DBUS_TYPE_UINT32, &minor,
101 dbus_connection_send(sd->session->manager->bus, m, NULL);
104 static int sd_eviocrevoke(int fd) {
110 r = ioctl(fd, EVIOCREVOKE, 1);
113 if (r == -EINVAL && !warned) {
115 log_warning("kernel does not support evdev-revocation");
122 static int sd_drmsetmaster(int fd) {
127 r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
134 static int sd_drmdropmaster(int fd) {
139 r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
146 static int session_device_open(SessionDevice *sd, bool active) {
149 assert(sd->type != DEVICE_TYPE_UNKNOWN);
151 /* open device and try to get an udev_device from it */
152 fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
157 case DEVICE_TYPE_DRM:
159 /* Weird legacy DRM semantics might return an error
160 * even though we're master. No way to detect that so
161 * fail at all times and let caller retry in inactive
163 r = sd_drmsetmaster(fd);
169 /* DRM-Master is granted to the first user who opens a
170 * device automatically (ughh, racy!). Hence, we just
171 * drop DRM-Master in case we were the first. */
172 sd_drmdropmaster(fd);
175 case DEVICE_TYPE_EVDEV:
179 case DEVICE_TYPE_FBDEV:
180 case DEVICE_TYPE_UNKNOWN:
182 /* fallback for devices wihout synchronizations */
189 static int session_device_start(SessionDevice *sd) {
193 assert(session_is_active(sd->session));
199 case DEVICE_TYPE_DRM:
200 /* Device is kept open. Simply call drmSetMaster() and hope
201 * there is no-one else. In case it fails, we keep the device
202 * paused. Maybe at some point we have a drmStealMaster(). */
203 r = sd_drmsetmaster(sd->fd);
207 case DEVICE_TYPE_EVDEV:
208 /* Evdev devices are revoked while inactive. Reopen it and we
210 r = session_device_open(sd, true);
213 close_nointr_nofail(sd->fd);
216 case DEVICE_TYPE_FBDEV:
217 /* fbdev devices have no way to synchronize access. Moreover,
218 * they mostly operate through mmaps() without any pageflips
219 * and modesetting, so there is no way for us to prevent access
220 * but tear down mmaps.
221 * That would be quite expensive to do on a per-fd context. So
222 * ignore legcy fbdev and let its users feel the pain they asked
223 * for when deciding for fbdev. */
224 case DEVICE_TYPE_UNKNOWN:
226 /* fallback for devices wihout synchronizations */
234 static void session_device_stop(SessionDevice *sd) {
241 case DEVICE_TYPE_DRM:
242 /* On DRM devices we simply drop DRM-Master but keep it open.
243 * This allows the user to keep resources allocated. The
244 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
245 * circumventing this. */
246 sd_drmdropmaster(sd->fd);
248 case DEVICE_TYPE_EVDEV:
249 /* Revoke access on evdev file-descriptors during deactivation.
250 * This will basically prevent any operations on the fd and
251 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
252 * protection this way. */
253 sd_eviocrevoke(sd->fd);
255 case DEVICE_TYPE_FBDEV:
256 case DEVICE_TYPE_UNKNOWN:
258 /* fallback for devices without synchronization */
265 static DeviceType detect_device_type(struct udev_device *dev) {
266 const char *sysname, *subsystem;
269 sysname = udev_device_get_sysname(dev);
270 subsystem = udev_device_get_subsystem(dev);
271 type = DEVICE_TYPE_UNKNOWN;
273 if (streq_ptr(subsystem, "graphics")) {
274 if (!streq(sysname, "fbcon") && startswith(sysname, "fb"))
275 type = DEVICE_TYPE_FBDEV;
276 } else if (streq_ptr(subsystem, "drm")) {
277 if (startswith(sysname, "card"))
278 type = DEVICE_TYPE_DRM;
279 } else if (streq_ptr(subsystem, "input")) {
280 if (startswith(sysname, "event"))
281 type = DEVICE_TYPE_EVDEV;
287 static int session_device_verify(SessionDevice *sd) {
288 struct udev_device *dev, *p = NULL;
289 const char *sp, *node;
292 dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
296 sp = udev_device_get_syspath(dev);
297 node = udev_device_get_devnode(dev);
303 /* detect device type so we can find the correct sysfs parent */
304 sd->type = detect_device_type(dev);
305 if (sd->type == DEVICE_TYPE_UNKNOWN) {
308 } else if (sd->type == DEVICE_TYPE_EVDEV) {
309 /* for evdev devices we need the parent node as device */
311 dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
316 sp = udev_device_get_syspath(dev);
317 } else if (sd->type != DEVICE_TYPE_FBDEV &&
318 sd->type != DEVICE_TYPE_DRM) {
319 /* Prevent opening unsupported devices. Especially devices of
320 * subsystem "input" must be opened via the evdev node as
321 * we require EVIOCREVOKE. */
326 /* search for an existing seat device and return it if available */
327 sd->device = hashmap_get(sd->session->manager->devices, sp);
329 /* The caller might have gotten the udev event before we were
330 * able to process it. Hence, fake the "add" event and let the
331 * logind-manager handle the new device. */
332 r = manager_process_seat_device(sd->session->manager, dev);
336 /* if it's still not available, then the device is invalid */
337 sd->device = hashmap_get(sd->session->manager->devices, sp);
344 if (sd->device->seat != sd->session->seat) {
349 sd->node = strdup(node);
357 udev_device_unref(p ? : dev);
361 int session_device_new(Session *s, dev_t dev, SessionDevice **out) {
371 sd = new0(SessionDevice, 1);
378 sd->type = DEVICE_TYPE_UNKNOWN;
380 r = session_device_verify(sd);
384 r = hashmap_put(s->devices, &sd->dev, sd);
390 /* Open the device for the first time. We need a valid fd to pass back
391 * to the caller. If the session is not active, this _might_ immediately
392 * revoke access and thus invalidate the fd. But this is still needed
393 * to pass a valid fd back. */
394 sd->active = session_is_active(s);
395 r = session_device_open(sd, sd->active);
397 /* EINVAL _may_ mean a master is active; retry inactive */
398 if (sd->active && r == -EINVAL) {
400 r = session_device_open(sd, false);
407 LIST_PREPEND(SessionDevice, sd_by_device, sd->device->session_devices, sd);
413 hashmap_remove(s->devices, &sd->dev);
419 void session_device_free(SessionDevice *sd) {
422 session_device_stop(sd);
423 session_device_notify(sd, SESSION_DEVICE_RELEASE);
424 close_nointr_nofail(sd->fd);
426 LIST_REMOVE(SessionDevice, sd_by_device, sd->device->session_devices, sd);
428 hashmap_remove(sd->session->devices, &sd->dev);
434 void session_device_complete_pause(SessionDevice *sd) {
441 session_device_stop(sd);
443 /* if not all devices are paused, wait for further completion events */
444 HASHMAP_FOREACH(iter, sd->session->devices, i)
448 /* complete any pending session switch */
449 seat_complete_switch(sd->session->seat);
452 void session_device_resume_all(Session *s) {
459 HASHMAP_FOREACH(sd, s->devices, i) {
461 r = session_device_start(sd);
463 session_device_notify(sd, SESSION_DEVICE_RESUME);
468 void session_device_pause_all(Session *s) {
474 HASHMAP_FOREACH(sd, s->devices, i) {
476 session_device_stop(sd);
477 session_device_notify(sd, SESSION_DEVICE_PAUSE);
482 unsigned int session_device_try_pause_all(Session *s) {
485 unsigned int num_pending = 0;
489 HASHMAP_FOREACH(sd, s->devices, i) {
491 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);