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>
29 #include "alloc-util.h"
32 #include "logind-session-device.h"
34 #include "parse-util.h"
35 #include "sd-daemon.h"
38 enum SessionDeviceNotifications
{
39 SESSION_DEVICE_RESUME
,
40 SESSION_DEVICE_TRY_PAUSE
,
42 SESSION_DEVICE_RELEASE
,
45 static int session_device_notify(SessionDevice
*sd
, enum SessionDeviceNotifications type
) {
46 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
47 _cleanup_free_
char *path
= NULL
;
49 uint32_t major
, minor
;
54 major
= major(sd
->dev
);
55 minor
= minor(sd
->dev
);
57 if (!sd
->session
->controller
)
60 path
= session_bus_path(sd
->session
);
64 r
= sd_bus_message_new_signal(
65 sd
->session
->manager
->bus
,
67 "org.freedesktop.login1.Session",
68 (type
== SESSION_DEVICE_RESUME
) ? "ResumeDevice" : "PauseDevice");
72 r
= sd_bus_message_set_destination(m
, sd
->session
->controller
);
78 case SESSION_DEVICE_RESUME
:
79 r
= sd_bus_message_append(m
, "uuh", major
, minor
, sd
->fd
);
84 case SESSION_DEVICE_TRY_PAUSE
:
88 case SESSION_DEVICE_PAUSE
:
92 case SESSION_DEVICE_RELEASE
:
101 r
= sd_bus_message_append(m
, "uus", major
, minor
, t
);
106 return sd_bus_send(sd
->session
->manager
->bus
, m
, NULL
);
109 static void sd_eviocrevoke(int fd
) {
110 static bool warned
= false;
114 if (ioctl(fd
, EVIOCREVOKE
, NULL
) < 0) {
116 if (errno
== EINVAL
&& !warned
) {
117 log_warning_errno(errno
, "Kernel does not support evdev-revocation: %m");
123 static int sd_drmsetmaster(int fd
) {
126 if (ioctl(fd
, DRM_IOCTL_SET_MASTER
, 0) < 0)
132 static int sd_drmdropmaster(int fd
) {
135 if (ioctl(fd
, DRM_IOCTL_DROP_MASTER
, 0) < 0)
141 static int session_device_open(SessionDevice
*sd
, bool active
) {
145 assert(sd
->type
!= DEVICE_TYPE_UNKNOWN
);
148 /* open device and try to get an udev_device from it */
149 fd
= open(sd
->node
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
155 case DEVICE_TYPE_DRM
:
157 /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
158 * that so fail at all times and let caller retry in inactive state. */
159 r
= sd_drmsetmaster(fd
);
165 /* DRM-Master is granted to the first user who opens a device automatically (ughh,
166 * racy!). Hence, we just drop DRM-Master in case we were the first. */
167 (void) sd_drmdropmaster(fd
);
170 case DEVICE_TYPE_EVDEV
:
175 case DEVICE_TYPE_UNKNOWN
:
177 /* fallback for devices wihout synchronizations */
184 static int session_device_start(SessionDevice
*sd
) {
188 assert(session_is_active(sd
->session
));
195 case DEVICE_TYPE_DRM
:
197 log_error("Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
201 /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
202 * keep the device paused. Maybe at some point we have a drmStealMaster(). */
203 r
= sd_drmsetmaster(sd
->fd
);
208 case DEVICE_TYPE_EVDEV
:
209 /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
210 r
= session_device_open(sd
, true);
214 /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
215 * into a session and logind has been restarted right before. */
220 case DEVICE_TYPE_UNKNOWN
:
222 /* fallback for devices without synchronizations */
230 static void session_device_stop(SessionDevice
*sd
) {
238 case DEVICE_TYPE_DRM
:
240 log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
244 /* On DRM devices we simply drop DRM-Master but keep it open.
245 * This allows the user to keep resources allocated. The
246 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
247 * circumventing this. */
248 sd_drmdropmaster(sd
->fd
);
251 case DEVICE_TYPE_EVDEV
:
252 /* Revoke access on evdev file-descriptors during deactivation.
253 * This will basically prevent any operations on the fd and
254 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
255 * protection this way. */
256 sd_eviocrevoke(sd
->fd
);
259 case DEVICE_TYPE_UNKNOWN
:
261 /* fallback for devices without synchronization */
268 static DeviceType
detect_device_type(struct udev_device
*dev
) {
269 const char *sysname
, *subsystem
;
272 sysname
= udev_device_get_sysname(dev
);
273 subsystem
= udev_device_get_subsystem(dev
);
274 type
= DEVICE_TYPE_UNKNOWN
;
276 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_DRM
) {
318 /* Prevent opening unsupported devices. Especially devices of
319 * subsystem "input" must be opened via the evdev node as
320 * we require EVIOCREVOKE. */
325 /* search for an existing seat device and return it if available */
326 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
328 /* The caller might have gotten the udev event before we were
329 * able to process it. Hence, fake the "add" event and let the
330 * logind-manager handle the new device. */
331 r
= manager_process_seat_device(sd
->session
->manager
, dev
);
335 /* if it's still not available, then the device is invalid */
336 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
343 if (sd
->device
->seat
!= sd
->session
->seat
) {
348 sd
->node
= strdup(node
);
356 udev_device_unref(p
? : dev
);
360 int session_device_new(Session
*s
, dev_t dev
, bool open_device
, SessionDevice
**out
) {
370 sd
= new0(SessionDevice
, 1);
377 sd
->type
= DEVICE_TYPE_UNKNOWN
;
379 r
= session_device_verify(sd
);
383 r
= hashmap_put(s
->devices
, &sd
->dev
, sd
);
388 /* Open the device for the first time. We need a valid fd to pass back
389 * to the caller. If the session is not active, this _might_ immediately
390 * revoke access and thus invalidate the fd. But this is still needed
391 * to pass a valid fd back. */
392 sd
->active
= session_is_active(s
);
393 r
= session_device_open(sd
, sd
->active
);
395 /* EINVAL _may_ mean a master is active; retry inactive */
396 if (sd
->active
&& r
== -EINVAL
) {
398 r
= session_device_open(sd
, false);
406 LIST_PREPEND(sd_by_device
, sd
->device
->session_devices
, sd
);
412 hashmap_remove(s
->devices
, &sd
->dev
);
418 void session_device_free(SessionDevice
*sd
) {
421 /* Make sure to remove the pushed fd. */
423 _cleanup_free_
char *m
= NULL
;
427 /* Session ID does not contain separators. */
428 id
= sd
->session
->id
;
429 assert(*(id
+ strcspn(id
, "-\n")) == '\0');
431 r
= asprintf(&m
, "FDSTOREREMOVE=1\n"
432 "FDNAME=session-%s-device-%u-%u\n",
433 id
, major(sd
->dev
), minor(sd
->dev
));
435 (void) sd_notify(false, m
);
438 session_device_stop(sd
);
439 session_device_notify(sd
, SESSION_DEVICE_RELEASE
);
442 LIST_REMOVE(sd_by_device
, sd
->device
->session_devices
, sd
);
444 hashmap_remove(sd
->session
->devices
, &sd
->dev
);
450 void session_device_complete_pause(SessionDevice
*sd
) {
457 session_device_stop(sd
);
459 /* if not all devices are paused, wait for further completion events */
460 HASHMAP_FOREACH(iter
, sd
->session
->devices
, i
)
464 /* complete any pending session switch */
465 seat_complete_switch(sd
->session
->seat
);
468 void session_device_resume_all(Session
*s
) {
474 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
478 if (session_device_start(sd
) < 0)
480 if (session_device_save(sd
) < 0)
483 session_device_notify(sd
, SESSION_DEVICE_RESUME
);
487 void session_device_pause_all(Session
*s
) {
493 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
497 session_device_stop(sd
);
498 session_device_notify(sd
, SESSION_DEVICE_PAUSE
);
502 unsigned int session_device_try_pause_all(Session
*s
) {
503 unsigned num_pending
= 0;
509 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
513 session_device_notify(sd
, SESSION_DEVICE_TRY_PAUSE
);
520 int session_device_save(SessionDevice
*sd
) {
521 _cleanup_free_
char *m
= NULL
;
527 /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
528 * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
529 * don't have to handle them differently later.
531 * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
537 /* Session ID does not contain separators. */
538 id
= sd
->session
->id
;
539 assert(*(id
+ strcspn(id
, "-\n")) == '\0');
541 r
= asprintf(&m
, "FDSTORE=1\n"
542 "FDNAME=session-%s-device-%u-%u\n",
543 id
, major(sd
->dev
), minor(sd
->dev
));
547 r
= sd_pid_notify_with_fds(0, false, m
, &sd
->fd
, 1);
551 sd
->pushed_fd
= true;
555 void session_device_attach_fd(SessionDevice
*sd
, int fd
, bool active
) {
562 sd
->pushed_fd
= true;