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
);
77 case SESSION_DEVICE_RESUME
:
78 r
= sd_bus_message_append(m
, "uuh", major
, minor
, sd
->fd
);
82 case SESSION_DEVICE_TRY_PAUSE
:
85 case SESSION_DEVICE_PAUSE
:
88 case SESSION_DEVICE_RELEASE
:
96 r
= sd_bus_message_append(m
, "uus", major
, minor
, t
);
101 return sd_bus_send(sd
->session
->manager
->bus
, m
, NULL
);
104 static int sd_eviocrevoke(int fd
) {
110 r
= ioctl(fd
, EVIOCREVOKE
, NULL
);
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_UNKNOWN
:
181 /* fallback for devices wihout synchronizations */
188 static int session_device_start(SessionDevice
*sd
) {
192 assert(session_is_active(sd
->session
));
198 case DEVICE_TYPE_DRM
:
199 /* Device is kept open. Simply call drmSetMaster() and hope
200 * there is no-one else. In case it fails, we keep the device
201 * paused. Maybe at some point we have a drmStealMaster(). */
202 r
= sd_drmsetmaster(sd
->fd
);
206 case DEVICE_TYPE_EVDEV
:
207 /* Evdev devices are revoked while inactive. Reopen it and we
209 r
= session_device_open(sd
, true);
212 /* For evdev devices, the file descriptor might be left
213 * uninitialized. This might happen while resuming into a
214 * session and logind has been restarted right before. */
218 case DEVICE_TYPE_UNKNOWN
:
220 /* fallback for devices wihout synchronizations */
228 static void session_device_stop(SessionDevice
*sd
) {
235 case DEVICE_TYPE_DRM
:
236 /* On DRM devices we simply drop DRM-Master but keep it open.
237 * This allows the user to keep resources allocated. The
238 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
239 * circumventing this. */
240 sd_drmdropmaster(sd
->fd
);
242 case DEVICE_TYPE_EVDEV
:
243 /* Revoke access on evdev file-descriptors during deactivation.
244 * This will basically prevent any operations on the fd and
245 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
246 * protection this way. */
247 sd_eviocrevoke(sd
->fd
);
249 case DEVICE_TYPE_UNKNOWN
:
251 /* fallback for devices without synchronization */
258 static DeviceType
detect_device_type(struct udev_device
*dev
) {
259 const char *sysname
, *subsystem
;
262 sysname
= udev_device_get_sysname(dev
);
263 subsystem
= udev_device_get_subsystem(dev
);
264 type
= DEVICE_TYPE_UNKNOWN
;
266 if (streq_ptr(subsystem
, "drm")) {
267 if (startswith(sysname
, "card"))
268 type
= DEVICE_TYPE_DRM
;
269 } else if (streq_ptr(subsystem
, "input")) {
270 if (startswith(sysname
, "event"))
271 type
= DEVICE_TYPE_EVDEV
;
277 static int session_device_verify(SessionDevice
*sd
) {
278 struct udev_device
*dev
, *p
= NULL
;
279 const char *sp
, *node
;
282 dev
= udev_device_new_from_devnum(sd
->session
->manager
->udev
, 'c', sd
->dev
);
286 sp
= udev_device_get_syspath(dev
);
287 node
= udev_device_get_devnode(dev
);
293 /* detect device type so we can find the correct sysfs parent */
294 sd
->type
= detect_device_type(dev
);
295 if (sd
->type
== DEVICE_TYPE_UNKNOWN
) {
298 } else if (sd
->type
== DEVICE_TYPE_EVDEV
) {
299 /* for evdev devices we need the parent node as device */
301 dev
= udev_device_get_parent_with_subsystem_devtype(p
, "input", NULL
);
306 sp
= udev_device_get_syspath(dev
);
307 } else if (sd
->type
!= DEVICE_TYPE_DRM
) {
308 /* Prevent opening unsupported devices. Especially devices of
309 * subsystem "input" must be opened via the evdev node as
310 * we require EVIOCREVOKE. */
315 /* search for an existing seat device and return it if available */
316 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
318 /* The caller might have gotten the udev event before we were
319 * able to process it. Hence, fake the "add" event and let the
320 * logind-manager handle the new device. */
321 r
= manager_process_seat_device(sd
->session
->manager
, dev
);
325 /* if it's still not available, then the device is invalid */
326 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
333 if (sd
->device
->seat
!= sd
->session
->seat
) {
338 sd
->node
= strdup(node
);
346 udev_device_unref(p
? : dev
);
350 int session_device_new(Session
*s
, dev_t dev
, bool open_device
, SessionDevice
**out
) {
360 sd
= new0(SessionDevice
, 1);
367 sd
->type
= DEVICE_TYPE_UNKNOWN
;
369 r
= session_device_verify(sd
);
373 r
= hashmap_put(s
->devices
, &sd
->dev
, sd
);
380 /* Open the device for the first time. We need a valid fd to pass back
381 * to the caller. If the session is not active, this _might_ immediately
382 * revoke access and thus invalidate the fd. But this is still needed
383 * to pass a valid fd back. */
384 sd
->active
= session_is_active(s
);
385 r
= session_device_open(sd
, sd
->active
);
387 /* EINVAL _may_ mean a master is active; retry inactive */
388 if (sd
->active
&& r
== -EINVAL
) {
390 r
= session_device_open(sd
, false);
398 LIST_PREPEND(sd_by_device
, sd
->device
->session_devices
, sd
);
404 hashmap_remove(s
->devices
, &sd
->dev
);
410 void session_device_free(SessionDevice
*sd
) {
416 /* Remove the pushed fd again, just in case. */
418 m
= strjoina("FDSTOREREMOVE=1\n"
419 "FDNAME=session-", sd
->session
->id
);
421 (void) sd_notify(false, m
);
424 session_device_stop(sd
);
425 session_device_notify(sd
, SESSION_DEVICE_RELEASE
);
426 close_nointr(sd
->fd
);
428 LIST_REMOVE(sd_by_device
, sd
->device
->session_devices
, sd
);
430 hashmap_remove(sd
->session
->devices
, &sd
->dev
);
436 void session_device_complete_pause(SessionDevice
*sd
) {
443 session_device_stop(sd
);
445 /* if not all devices are paused, wait for further completion events */
446 HASHMAP_FOREACH(iter
, sd
->session
->devices
, i
)
450 /* complete any pending session switch */
451 seat_complete_switch(sd
->session
->seat
);
454 void session_device_resume_all(Session
*s
) {
460 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
462 if (session_device_start(sd
) < 0)
464 if (session_device_save(sd
) < 0)
466 session_device_notify(sd
, SESSION_DEVICE_RESUME
);
471 void session_device_pause_all(Session
*s
) {
477 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
479 session_device_stop(sd
);
480 session_device_notify(sd
, SESSION_DEVICE_PAUSE
);
485 unsigned int session_device_try_pause_all(Session
*s
) {
488 unsigned int num_pending
= 0;
492 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
494 session_device_notify(sd
, SESSION_DEVICE_TRY_PAUSE
);
502 int session_device_save(SessionDevice
*sd
) {
508 /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
509 * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
510 * don't have to handle them differently later.
512 * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
518 m
= strjoina("FDSTORE=1\n"
519 "FDNAME=session", sd
->session
->id
);
521 r
= sd_pid_notify_with_fds(0, false, m
, &sd
->fd
, 1);
525 sd
->pushed_fd
= true;
529 void session_device_attach_fd(SessionDevice
*sd
, int fd
, bool active
) {