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);
81 case SESSION_DEVICE_RESUME:
82 r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
86 case SESSION_DEVICE_TRY_PAUSE:
89 case SESSION_DEVICE_PAUSE:
92 case SESSION_DEVICE_RELEASE:
100 r = sd_bus_message_append(m, "uus", major, minor, t);
105 return sd_bus_send(sd->session->manager->bus, m, NULL);
108 static int sd_eviocrevoke(int fd) {
114 r = ioctl(fd, EVIOCREVOKE, NULL);
117 if (r == -EINVAL && !warned) {
119 log_warning("kernel does not support evdev-revocation");
126 static int sd_drmsetmaster(int fd) {
131 r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
138 static int sd_drmdropmaster(int fd) {
143 r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
150 static int session_device_open(SessionDevice *sd, bool active) {
153 assert(sd->type != DEVICE_TYPE_UNKNOWN);
155 /* open device and try to get an udev_device from it */
156 fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
161 case DEVICE_TYPE_DRM:
163 /* Weird legacy DRM semantics might return an error
164 * even though we're master. No way to detect that so
165 * fail at all times and let caller retry in inactive
167 r = sd_drmsetmaster(fd);
173 /* DRM-Master is granted to the first user who opens a
174 * device automatically (ughh, racy!). Hence, we just
175 * drop DRM-Master in case we were the first. */
176 sd_drmdropmaster(fd);
179 case DEVICE_TYPE_EVDEV:
183 case DEVICE_TYPE_UNKNOWN:
185 /* fallback for devices wihout synchronizations */
192 static int session_device_start(SessionDevice *sd) {
196 assert(session_is_active(sd->session));
202 case DEVICE_TYPE_DRM:
203 /* Device is kept open. Simply call drmSetMaster() and hope
204 * there is no-one else. In case it fails, we keep the device
205 * paused. Maybe at some point we have a drmStealMaster(). */
206 r = sd_drmsetmaster(sd->fd);
210 case DEVICE_TYPE_EVDEV:
211 /* Evdev devices are revoked while inactive. Reopen it and we
213 r = session_device_open(sd, true);
216 /* For evdev devices, the file descriptor might be left
217 * uninitialized. This might happen while resuming into a
218 * session and logind has been restarted right before. */
222 case DEVICE_TYPE_UNKNOWN:
224 /* fallback for devices wihout synchronizations */
232 static void session_device_stop(SessionDevice *sd) {
239 case DEVICE_TYPE_DRM:
240 /* On DRM devices we simply drop DRM-Master but keep it open.
241 * This allows the user to keep resources allocated. The
242 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
243 * circumventing this. */
244 sd_drmdropmaster(sd->fd);
246 case DEVICE_TYPE_EVDEV:
247 /* Revoke access on evdev file-descriptors during deactivation.
248 * This will basically prevent any operations on the fd and
249 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
250 * protection this way. */
251 sd_eviocrevoke(sd->fd);
253 case DEVICE_TYPE_UNKNOWN:
255 /* fallback for devices without synchronization */
262 static DeviceType detect_device_type(struct udev_device *dev) {
263 const char *sysname, *subsystem;
266 sysname = udev_device_get_sysname(dev);
267 subsystem = udev_device_get_subsystem(dev);
268 type = DEVICE_TYPE_UNKNOWN;
270 if (streq_ptr(subsystem, "drm")) {
271 if (startswith(sysname, "card"))
272 type = DEVICE_TYPE_DRM;
273 } else if (streq_ptr(subsystem, "input")) {
274 if (startswith(sysname, "event"))
275 type = DEVICE_TYPE_EVDEV;
281 static int session_device_verify(SessionDevice *sd) {
282 struct udev_device *dev, *p = NULL;
283 const char *sp, *node;
286 dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
290 sp = udev_device_get_syspath(dev);
291 node = udev_device_get_devnode(dev);
297 /* detect device type so we can find the correct sysfs parent */
298 sd->type = detect_device_type(dev);
299 if (sd->type == DEVICE_TYPE_UNKNOWN) {
302 } else if (sd->type == DEVICE_TYPE_EVDEV) {
303 /* for evdev devices we need the parent node as device */
305 dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
310 sp = udev_device_get_syspath(dev);
311 } else if (sd->type != DEVICE_TYPE_DRM) {
312 /* Prevent opening unsupported devices. Especially devices of
313 * subsystem "input" must be opened via the evdev node as
314 * we require EVIOCREVOKE. */
319 /* search for an existing seat device and return it if available */
320 sd->device = hashmap_get(sd->session->manager->devices, sp);
322 /* The caller might have gotten the udev event before we were
323 * able to process it. Hence, fake the "add" event and let the
324 * logind-manager handle the new device. */
325 r = manager_process_seat_device(sd->session->manager, dev);
329 /* if it's still not available, then the device is invalid */
330 sd->device = hashmap_get(sd->session->manager->devices, sp);
337 if (sd->device->seat != sd->session->seat) {
342 sd->node = strdup(node);
350 udev_device_unref(p ? : dev);
354 int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
364 sd = new0(SessionDevice, 1);
371 sd->type = DEVICE_TYPE_UNKNOWN;
373 r = session_device_verify(sd);
377 r = hashmap_put(s->devices, &sd->dev, sd);
384 /* Open the device for the first time. We need a valid fd to pass back
385 * to the caller. If the session is not active, this _might_ immediately
386 * revoke access and thus invalidate the fd. But this is still needed
387 * to pass a valid fd back. */
388 sd->active = session_is_active(s);
389 r = session_device_open(sd, sd->active);
391 /* EINVAL _may_ mean a master is active; retry inactive */
392 if (sd->active && r == -EINVAL) {
394 r = session_device_open(sd, false);
402 LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
408 hashmap_remove(s->devices, &sd->dev);
414 void session_device_free(SessionDevice *sd) {
420 /* Remove the pushed fd again, just in case. */
422 m = strjoina("FDSTOREREMOVE=1\n"
423 "FDNAME=session-", sd->session->id);
425 (void) sd_notify(false, m);
428 session_device_stop(sd);
429 session_device_notify(sd, SESSION_DEVICE_RELEASE);
430 close_nointr(sd->fd);
432 LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
434 hashmap_remove(sd->session->devices, &sd->dev);
440 void session_device_complete_pause(SessionDevice *sd) {
447 session_device_stop(sd);
449 /* if not all devices are paused, wait for further completion events */
450 HASHMAP_FOREACH(iter, sd->session->devices, i)
454 /* complete any pending session switch */
455 seat_complete_switch(sd->session->seat);
458 void session_device_resume_all(Session *s) {
464 HASHMAP_FOREACH(sd, s->devices, i) {
466 if (session_device_start(sd) < 0)
468 if (session_device_save(sd) < 0)
470 session_device_notify(sd, SESSION_DEVICE_RESUME);
475 void session_device_pause_all(Session *s) {
481 HASHMAP_FOREACH(sd, s->devices, i) {
483 session_device_stop(sd);
484 session_device_notify(sd, SESSION_DEVICE_PAUSE);
489 unsigned int session_device_try_pause_all(Session *s) {
492 unsigned int num_pending = 0;
496 HASHMAP_FOREACH(sd, s->devices, i) {
498 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
506 int session_device_save(SessionDevice *sd) {
512 /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
513 * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
514 * don't have to handle them differently later.
516 * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
522 m = strjoina("FDSTORE=1\n"
523 "FDNAME=session", sd->session->id);
525 r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
529 sd->pushed_fd = true;
533 void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {