1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 David Herrmann
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/input.h>
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
27 #if 0 /// elogind needs the systems udev header
33 #include "alloc-util.h"
36 #include "logind-session-device.h"
38 #include "parse-util.h"
39 #include "sd-daemon.h"
42 enum SessionDeviceNotifications {
43 SESSION_DEVICE_RESUME,
44 SESSION_DEVICE_TRY_PAUSE,
46 SESSION_DEVICE_RELEASE,
49 static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
50 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
51 _cleanup_free_ char *path = NULL;
53 uint32_t major, minor;
58 major = major(sd->dev);
59 minor = minor(sd->dev);
61 if (!sd->session->controller)
64 path = session_bus_path(sd->session);
68 r = sd_bus_message_new_signal(
69 sd->session->manager->bus,
71 "org.freedesktop.login1.Session",
72 (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
76 r = sd_bus_message_set_destination(m, sd->session->controller);
82 case SESSION_DEVICE_RESUME:
83 r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
88 case SESSION_DEVICE_TRY_PAUSE:
92 case SESSION_DEVICE_PAUSE:
96 case SESSION_DEVICE_RELEASE:
105 r = sd_bus_message_append(m, "uus", major, minor, t);
110 return sd_bus_send(sd->session->manager->bus, m, NULL);
113 static void sd_eviocrevoke(int fd) {
114 static bool warned = false;
118 if (ioctl(fd, EVIOCREVOKE, NULL) < 0) {
120 if (errno == EINVAL && !warned) {
121 log_warning_errno(errno, "Kernel does not support evdev-revocation: %m");
127 static int sd_drmsetmaster(int fd) {
130 if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0)
136 static int sd_drmdropmaster(int fd) {
139 if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) < 0)
145 static int session_device_open(SessionDevice *sd, bool active) {
149 assert(sd->type != DEVICE_TYPE_UNKNOWN);
152 /* open device and try to get an udev_device from it */
153 fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
159 case DEVICE_TYPE_DRM:
161 /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
162 * that so fail at all times and let caller retry in inactive state. */
163 r = sd_drmsetmaster(fd);
169 /* DRM-Master is granted to the first user who opens a device automatically (ughh,
170 * racy!). Hence, we just drop DRM-Master in case we were the first. */
171 (void) sd_drmdropmaster(fd);
174 case DEVICE_TYPE_EVDEV:
179 case DEVICE_TYPE_UNKNOWN:
181 /* fallback for devices wihout synchronizations */
188 static int session_device_start(SessionDevice *sd) {
192 assert(session_is_active(sd->session));
199 case DEVICE_TYPE_DRM:
202 /* Open device if it isn't open yet */
203 sd->fd = session_device_open(sd, true);
207 /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
208 * keep the device paused. Maybe at some point we have a drmStealMaster(). */
209 r = sd_drmsetmaster(sd->fd);
215 case DEVICE_TYPE_EVDEV:
216 /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
217 r = session_device_open(sd, true);
221 /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
222 * into a session and logind has been restarted right before. */
227 case DEVICE_TYPE_UNKNOWN:
229 /* fallback for devices without synchronizations */
237 static void session_device_stop(SessionDevice *sd) {
245 case DEVICE_TYPE_DRM:
246 /* On DRM devices we simply drop DRM-Master but keep it open.
247 * This allows the user to keep resources allocated. The
248 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
249 * circumventing this. */
250 sd_drmdropmaster(sd->fd);
253 case DEVICE_TYPE_EVDEV:
254 /* Revoke access on evdev file-descriptors during deactivation.
255 * This will basically prevent any operations on the fd and
256 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
257 * protection this way. */
258 sd_eviocrevoke(sd->fd);
261 case DEVICE_TYPE_UNKNOWN:
263 /* fallback for devices without synchronization */
270 static DeviceType detect_device_type(struct udev_device *dev) {
271 const char *sysname, *subsystem;
274 sysname = udev_device_get_sysname(dev);
275 subsystem = udev_device_get_subsystem(dev);
276 type = DEVICE_TYPE_UNKNOWN;
278 if (streq_ptr(subsystem, "drm")) {
279 if (startswith(sysname, "card"))
280 type = DEVICE_TYPE_DRM;
281 } else if (streq_ptr(subsystem, "input")) {
282 if (startswith(sysname, "event"))
283 type = DEVICE_TYPE_EVDEV;
289 static int session_device_verify(SessionDevice *sd) {
290 struct udev_device *dev, *p = NULL;
291 const char *sp, *node;
294 dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
298 sp = udev_device_get_syspath(dev);
299 node = udev_device_get_devnode(dev);
305 /* detect device type so we can find the correct sysfs parent */
306 sd->type = detect_device_type(dev);
307 if (sd->type == DEVICE_TYPE_UNKNOWN) {
310 } else if (sd->type == DEVICE_TYPE_EVDEV) {
311 /* for evdev devices we need the parent node as device */
313 dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
318 sp = udev_device_get_syspath(dev);
319 } else if (sd->type != DEVICE_TYPE_DRM) {
320 /* Prevent opening unsupported devices. Especially devices of
321 * subsystem "input" must be opened via the evdev node as
322 * we require EVIOCREVOKE. */
327 /* search for an existing seat device and return it if available */
328 sd->device = hashmap_get(sd->session->manager->devices, sp);
330 /* The caller might have gotten the udev event before we were
331 * able to process it. Hence, fake the "add" event and let the
332 * logind-manager handle the new device. */
333 r = manager_process_seat_device(sd->session->manager, dev);
337 /* if it's still not available, then the device is invalid */
338 sd->device = hashmap_get(sd->session->manager->devices, sp);
345 if (sd->device->seat != sd->session->seat) {
350 sd->node = strdup(node);
358 udev_device_unref(p ? : dev);
362 int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
372 sd = new0(SessionDevice, 1);
379 sd->type = DEVICE_TYPE_UNKNOWN;
381 r = session_device_verify(sd);
385 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);
408 LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
414 hashmap_remove(s->devices, &sd->dev);
420 void session_device_free(SessionDevice *sd) {
426 /* Remove the pushed fd again, just in case. */
428 m = strjoina("FDSTOREREMOVE=1\n"
429 "FDNAME=session-", sd->session->id);
431 (void) sd_notify(false, m);
434 session_device_stop(sd);
435 session_device_notify(sd, SESSION_DEVICE_RELEASE);
438 LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
440 hashmap_remove(sd->session->devices, &sd->dev);
446 void session_device_complete_pause(SessionDevice *sd) {
453 session_device_stop(sd);
455 /* if not all devices are paused, wait for further completion events */
456 HASHMAP_FOREACH(iter, sd->session->devices, i)
460 /* complete any pending session switch */
461 seat_complete_switch(sd->session->seat);
464 void session_device_resume_all(Session *s) {
470 HASHMAP_FOREACH(sd, s->devices, i) {
474 if (session_device_start(sd) < 0)
476 if (session_device_save(sd) < 0)
479 session_device_notify(sd, SESSION_DEVICE_RESUME);
483 void session_device_pause_all(Session *s) {
489 HASHMAP_FOREACH(sd, s->devices, i) {
493 session_device_stop(sd);
494 session_device_notify(sd, SESSION_DEVICE_PAUSE);
498 unsigned int session_device_try_pause_all(Session *s) {
499 unsigned num_pending = 0;
505 HASHMAP_FOREACH(sd, s->devices, i) {
509 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
516 int session_device_save(SessionDevice *sd) {
522 /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
523 * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
524 * don't have to handle them differently later.
526 * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
532 m = strjoina("FDSTORE=1\n"
533 "FDNAME=session", sd->session->id);
535 r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
539 sd->pushed_fd = true;
543 void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {