1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 #include "alloc-util.h"
13 #include "daemon-util.h"
14 #include "device-util.h"
16 #include "logind-session-dbus.h"
17 #include "logind-session-device.h"
18 #include "missing_drm.h"
19 #include "missing_input.h"
20 #include "parse-util.h"
22 enum SessionDeviceNotifications
{
23 SESSION_DEVICE_RESUME
,
24 SESSION_DEVICE_TRY_PAUSE
,
26 SESSION_DEVICE_RELEASE
,
29 static int session_device_notify(SessionDevice
*sd
, enum SessionDeviceNotifications type
) {
30 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
31 _cleanup_free_
char *path
= NULL
;
33 uint32_t major
, minor
;
38 major
= major(sd
->dev
);
39 minor
= minor(sd
->dev
);
41 if (!sd
->session
->controller
)
44 path
= session_bus_path(sd
->session
);
48 r
= sd_bus_message_new_signal(
49 sd
->session
->manager
->bus
,
51 "org.freedesktop.login1.Session",
52 type
== SESSION_DEVICE_RESUME
? "ResumeDevice" : "PauseDevice");
56 r
= sd_bus_message_set_destination(m
, sd
->session
->controller
);
62 case SESSION_DEVICE_RESUME
:
63 r
= sd_bus_message_append(m
, "uuh", major
, minor
, sd
->fd
);
68 case SESSION_DEVICE_TRY_PAUSE
:
72 case SESSION_DEVICE_PAUSE
:
76 case SESSION_DEVICE_RELEASE
:
85 r
= sd_bus_message_append(m
, "uus", major
, minor
, t
);
90 return sd_bus_send(sd
->session
->manager
->bus
, m
, NULL
);
93 static void sd_eviocrevoke(int fd
) {
94 static bool warned
= false;
98 if (ioctl(fd
, EVIOCREVOKE
, NULL
) < 0) {
100 if (errno
== EINVAL
&& !warned
) {
101 log_warning_errno(errno
, "Kernel does not support evdev-revocation: %m");
107 static int sd_drmsetmaster(int fd
) {
109 return RET_NERRNO(ioctl(fd
, DRM_IOCTL_SET_MASTER
, 0));
112 static int sd_drmdropmaster(int fd
) {
114 return RET_NERRNO(ioctl(fd
, DRM_IOCTL_DROP_MASTER
, 0));
117 static int session_device_open(SessionDevice
*sd
, bool active
) {
121 assert(sd
->type
!= DEVICE_TYPE_UNKNOWN
);
124 /* open device and try to get a udev_device from it */
125 fd
= open(sd
->node
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
131 case DEVICE_TYPE_DRM
:
133 /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
134 * that so fail at all times and let caller retry in inactive state. */
135 r
= sd_drmsetmaster(fd
);
137 (void) close_nointr(fd
);
141 /* DRM-Master is granted to the first user who opens a device automatically (ughh,
142 * racy!). Hence, we just drop DRM-Master in case we were the first. */
143 (void) sd_drmdropmaster(fd
);
146 case DEVICE_TYPE_EVDEV
:
151 case DEVICE_TYPE_UNKNOWN
:
153 /* fallback for devices without synchronizations */
160 static int session_device_start(SessionDevice
*sd
) {
164 assert(session_is_active(sd
->session
));
171 case DEVICE_TYPE_DRM
:
173 return log_error_errno(SYNTHETIC_ERRNO(EBADF
),
174 "Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
176 /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
177 * keep the device paused. Maybe at some point we have a drmStealMaster(). */
178 r
= sd_drmsetmaster(sd
->fd
);
183 case DEVICE_TYPE_EVDEV
:
184 /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
185 r
= session_device_open(sd
, true);
189 /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
190 * into a session and logind has been restarted right before. */
191 close_and_replace(sd
->fd
, r
);
194 case DEVICE_TYPE_UNKNOWN
:
196 /* fallback for devices without synchronizations */
204 static void session_device_stop(SessionDevice
*sd
) {
212 case DEVICE_TYPE_DRM
:
214 log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
218 /* On DRM devices we simply drop DRM-Master but keep it open.
219 * This allows the user to keep resources allocated. The
220 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
221 * circumventing this. */
222 sd_drmdropmaster(sd
->fd
);
225 case DEVICE_TYPE_EVDEV
:
226 /* Revoke access on evdev file-descriptors during deactivation.
227 * This will basically prevent any operations on the fd and
228 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
229 * protection this way. */
230 sd_eviocrevoke(sd
->fd
);
233 case DEVICE_TYPE_UNKNOWN
:
235 /* fallback for devices without synchronization */
242 static DeviceType
detect_device_type(sd_device
*dev
) {
245 if (sd_device_get_sysname(dev
, &sysname
) < 0)
246 return DEVICE_TYPE_UNKNOWN
;
248 if (device_in_subsystem(dev
, "drm")) {
249 if (startswith(sysname
, "card"))
250 return DEVICE_TYPE_DRM
;
252 } else if (device_in_subsystem(dev
, "input")) {
253 if (startswith(sysname
, "event"))
254 return DEVICE_TYPE_EVDEV
;
257 return DEVICE_TYPE_UNKNOWN
;
260 static int session_device_verify(SessionDevice
*sd
) {
261 _cleanup_(sd_device_unrefp
) sd_device
*p
= NULL
;
262 const char *sp
, *node
;
266 r
= sd_device_new_from_devnum(&p
, 'c', sd
->dev
);
272 if (sd_device_get_syspath(dev
, &sp
) < 0 ||
273 sd_device_get_devname(dev
, &node
) < 0)
276 /* detect device type so we can find the correct sysfs parent */
277 sd
->type
= detect_device_type(dev
);
279 /* Prevent opening unsupported devices. Especially devices of
280 * subsystem "input" must be opened via the evdev node as
281 * we require EVIOCREVOKE. */
283 case DEVICE_TYPE_EVDEV
:
284 /* for evdev devices we need the parent node as device */
285 if (sd_device_get_parent_with_subsystem_devtype(p
, "input", NULL
, &dev
) < 0)
287 if (sd_device_get_syspath(dev
, &sp
) < 0)
291 case DEVICE_TYPE_DRM
:
294 case DEVICE_TYPE_UNKNOWN
:
299 /* search for an existing seat device and return it if available */
300 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
302 /* The caller might have gotten the udev event before we were
303 * able to process it. Hence, fake the "add" event and let the
304 * logind-manager handle the new device. */
305 r
= manager_process_seat_device(sd
->session
->manager
, dev
);
309 /* if it's still not available, then the device is invalid */
310 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
315 if (sd
->device
->seat
!= sd
->session
->seat
)
318 sd
->node
= strdup(node
);
325 int session_device_new(Session
*s
, dev_t dev
, bool open_device
, SessionDevice
**out
) {
335 sd
= new0(SessionDevice
, 1);
342 sd
->type
= DEVICE_TYPE_UNKNOWN
;
344 r
= session_device_verify(sd
);
348 r
= hashmap_put(s
->devices
, &sd
->dev
, sd
);
353 /* Open the device for the first time. We need a valid fd to pass back
354 * to the caller. If the session is not active, this _might_ immediately
355 * revoke access and thus invalidate the fd. But this is still needed
356 * to pass a valid fd back. */
357 sd
->active
= session_is_active(s
);
358 r
= session_device_open(sd
, sd
->active
);
360 /* EINVAL _may_ mean a master is active; retry inactive */
361 if (sd
->active
&& r
== -EINVAL
) {
363 r
= session_device_open(sd
, false);
371 LIST_PREPEND(sd_by_device
, sd
->device
->session_devices
, sd
);
377 hashmap_remove(s
->devices
, &sd
->dev
);
383 SessionDevice
*session_device_free(SessionDevice
*sd
) {
387 /* Make sure to remove the pushed fd. */
389 (void) notify_remove_fd_warnf("session-%s-device-%u-%u", sd
->session
->id
, major(sd
->dev
), minor(sd
->dev
));
391 session_device_stop(sd
);
392 session_device_notify(sd
, SESSION_DEVICE_RELEASE
);
395 LIST_REMOVE(sd_by_device
, sd
->device
->session_devices
, sd
);
397 hashmap_remove(sd
->session
->devices
, &sd
->dev
);
404 void session_device_complete_pause(SessionDevice
*sd
) {
410 session_device_stop(sd
);
412 /* if not all devices are paused, wait for further completion events */
413 HASHMAP_FOREACH(iter
, sd
->session
->devices
)
417 /* complete any pending session switch */
418 seat_complete_switch(sd
->session
->seat
);
421 void session_device_resume_all(Session
*s
) {
426 HASHMAP_FOREACH(sd
, s
->devices
) {
430 if (session_device_start(sd
) < 0)
432 if (session_device_save(sd
) < 0)
435 session_device_notify(sd
, SESSION_DEVICE_RESUME
);
439 void session_device_pause_all(Session
*s
) {
444 HASHMAP_FOREACH(sd
, s
->devices
) {
448 session_device_stop(sd
);
449 session_device_notify(sd
, SESSION_DEVICE_PAUSE
);
453 unsigned session_device_try_pause_all(Session
*s
) {
454 unsigned num_pending
= 0;
459 HASHMAP_FOREACH(sd
, s
->devices
) {
463 session_device_notify(sd
, SESSION_DEVICE_TRY_PAUSE
);
470 int session_device_save(SessionDevice
*sd
) {
476 /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
477 * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
478 * don't have to handle them differently later.
480 * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
486 /* Session ID does not contain separators. */
487 id
= sd
->session
->id
;
488 assert(*(id
+ strcspn(id
, "-\n")) == '\0');
490 r
= notify_push_fdf(sd
->fd
, "session-%s-device-%u-%u", id
, major(sd
->dev
), minor(sd
->dev
));
494 sd
->pushed_fd
= true;
498 void session_device_attach_fd(SessionDevice
*sd
, int fd
, bool active
) {
505 sd
->pushed_fd
= true;