1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 David Herrmann
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/input.h>
25 #include <sys/ioctl.h>
26 #include <sys/types.h>
32 #include "logind-session-device.h"
36 enum SessionDeviceNotifications
{
37 SESSION_DEVICE_RESUME
,
38 SESSION_DEVICE_TRY_PAUSE
,
40 SESSION_DEVICE_RELEASE
,
43 static int session_device_notify(SessionDevice
*sd
, enum SessionDeviceNotifications type
) {
44 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
45 _cleanup_free_
char *path
= NULL
;
47 uint32_t major
, minor
;
52 major
= major(sd
->dev
);
53 minor
= minor(sd
->dev
);
55 if (!sd
->session
->controller
)
58 path
= session_bus_path(sd
->session
);
62 r
= sd_bus_message_new_signal(
63 sd
->session
->manager
->bus
,
65 "org.freedesktop.login1.Session",
66 (type
== SESSION_DEVICE_RESUME
) ? "ResumeDevice" : "PauseDevice");
70 r
= sd_bus_message_set_destination(m
, sd
->session
->controller
);
75 case SESSION_DEVICE_RESUME
:
76 r
= sd_bus_message_append(m
, "uuh", major
, minor
, sd
->fd
);
80 case SESSION_DEVICE_TRY_PAUSE
:
83 case SESSION_DEVICE_PAUSE
:
86 case SESSION_DEVICE_RELEASE
:
94 r
= sd_bus_message_append(m
, "uus", major
, minor
, t
);
99 return sd_bus_send(sd
->session
->manager
->bus
, m
, NULL
);
102 static int sd_eviocrevoke(int fd
) {
108 r
= ioctl(fd
, EVIOCREVOKE
, NULL
);
111 if (r
== -EINVAL
&& !warned
) {
113 log_warning("kernel does not support evdev-revocation");
120 static int sd_drmsetmaster(int fd
) {
125 r
= ioctl(fd
, DRM_IOCTL_SET_MASTER
, 0);
132 static int sd_drmdropmaster(int fd
) {
137 r
= ioctl(fd
, DRM_IOCTL_DROP_MASTER
, 0);
144 static int session_device_open(SessionDevice
*sd
, bool active
) {
147 assert(sd
->type
!= DEVICE_TYPE_UNKNOWN
);
149 /* open device and try to get an udev_device from it */
150 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
158 * even though we're master. No way to detect that so
159 * fail at all times and let caller retry in inactive
161 r
= sd_drmsetmaster(fd
);
167 /* DRM-Master is granted to the first user who opens a
168 * device automatically (ughh, racy!). Hence, we just
169 * drop DRM-Master in case we were the first. */
170 sd_drmdropmaster(fd
);
173 case DEVICE_TYPE_EVDEV
:
177 case DEVICE_TYPE_UNKNOWN
:
179 /* fallback for devices wihout synchronizations */
186 static int session_device_start(SessionDevice
*sd
) {
190 assert(session_is_active(sd
->session
));
196 case DEVICE_TYPE_DRM
:
197 /* Device is kept open. Simply call drmSetMaster() and hope
198 * there is no-one else. In case it fails, we keep the device
199 * paused. Maybe at some point we have a drmStealMaster(). */
200 r
= sd_drmsetmaster(sd
->fd
);
204 case DEVICE_TYPE_EVDEV
:
205 /* Evdev devices are revoked while inactive. Reopen it and we
207 r
= session_device_open(sd
, true);
210 close_nointr(sd
->fd
);
213 case DEVICE_TYPE_UNKNOWN
:
215 /* fallback for devices wihout synchronizations */
223 static void session_device_stop(SessionDevice
*sd
) {
230 case DEVICE_TYPE_DRM
:
231 /* On DRM devices we simply drop DRM-Master but keep it open.
232 * This allows the user to keep resources allocated. The
233 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
234 * circumventing this. */
235 sd_drmdropmaster(sd
->fd
);
237 case DEVICE_TYPE_EVDEV
:
238 /* Revoke access on evdev file-descriptors during deactivation.
239 * This will basically prevent any operations on the fd and
240 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
241 * protection this way. */
242 sd_eviocrevoke(sd
->fd
);
244 case DEVICE_TYPE_UNKNOWN
:
246 /* fallback for devices without synchronization */
253 static DeviceType
detect_device_type(struct udev_device
*dev
) {
254 const char *sysname
, *subsystem
;
257 sysname
= udev_device_get_sysname(dev
);
258 subsystem
= udev_device_get_subsystem(dev
);
259 type
= DEVICE_TYPE_UNKNOWN
;
261 if (streq_ptr(subsystem
, "drm")) {
262 if (startswith(sysname
, "card"))
263 type
= DEVICE_TYPE_DRM
;
264 } else if (streq_ptr(subsystem
, "input")) {
265 if (startswith(sysname
, "event"))
266 type
= DEVICE_TYPE_EVDEV
;
272 static int session_device_verify(SessionDevice
*sd
) {
273 struct udev_device
*dev
, *p
= NULL
;
274 const char *sp
, *node
;
277 dev
= udev_device_new_from_devnum(sd
->session
->manager
->udev
, 'c', sd
->dev
);
281 sp
= udev_device_get_syspath(dev
);
282 node
= udev_device_get_devnode(dev
);
288 /* detect device type so we can find the correct sysfs parent */
289 sd
->type
= detect_device_type(dev
);
290 if (sd
->type
== DEVICE_TYPE_UNKNOWN
) {
293 } else if (sd
->type
== DEVICE_TYPE_EVDEV
) {
294 /* for evdev devices we need the parent node as device */
296 dev
= udev_device_get_parent_with_subsystem_devtype(p
, "input", NULL
);
301 sp
= udev_device_get_syspath(dev
);
302 } else if (sd
->type
!= DEVICE_TYPE_DRM
) {
303 /* Prevent opening unsupported devices. Especially devices of
304 * subsystem "input" must be opened via the evdev node as
305 * we require EVIOCREVOKE. */
310 /* search for an existing seat device and return it if available */
311 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
313 /* The caller might have gotten the udev event before we were
314 * able to process it. Hence, fake the "add" event and let the
315 * logind-manager handle the new device. */
316 r
= manager_process_seat_device(sd
->session
->manager
, dev
);
320 /* if it's still not available, then the device is invalid */
321 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
328 if (sd
->device
->seat
!= sd
->session
->seat
) {
333 sd
->node
= strdup(node
);
341 udev_device_unref(p
? : dev
);
345 int session_device_new(Session
*s
, dev_t dev
, SessionDevice
**out
) {
355 sd
= new0(SessionDevice
, 1);
362 sd
->type
= DEVICE_TYPE_UNKNOWN
;
364 r
= session_device_verify(sd
);
368 r
= hashmap_put(s
->devices
, &sd
->dev
, sd
);
374 /* Open the device for the first time. We need a valid fd to pass back
375 * to the caller. If the session is not active, this _might_ immediately
376 * revoke access and thus invalidate the fd. But this is still needed
377 * to pass a valid fd back. */
378 sd
->active
= session_is_active(s
);
379 r
= session_device_open(sd
, sd
->active
);
381 /* EINVAL _may_ mean a master is active; retry inactive */
382 if (sd
->active
&& r
== -EINVAL
) {
384 r
= session_device_open(sd
, false);
391 LIST_PREPEND(sd_by_device
, sd
->device
->session_devices
, sd
);
397 hashmap_remove(s
->devices
, &sd
->dev
);
403 void session_device_free(SessionDevice
*sd
) {
406 session_device_stop(sd
);
407 session_device_notify(sd
, SESSION_DEVICE_RELEASE
);
408 close_nointr(sd
->fd
);
410 LIST_REMOVE(sd_by_device
, sd
->device
->session_devices
, sd
);
412 hashmap_remove(sd
->session
->devices
, &sd
->dev
);
418 void session_device_complete_pause(SessionDevice
*sd
) {
425 session_device_stop(sd
);
427 /* if not all devices are paused, wait for further completion events */
428 HASHMAP_FOREACH(iter
, sd
->session
->devices
, i
)
432 /* complete any pending session switch */
433 seat_complete_switch(sd
->session
->seat
);
436 void session_device_resume_all(Session
*s
) {
443 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
445 r
= session_device_start(sd
);
447 session_device_notify(sd
, SESSION_DEVICE_RESUME
);
452 void session_device_pause_all(Session
*s
) {
458 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
460 session_device_stop(sd
);
461 session_device_notify(sd
, SESSION_DEVICE_PAUSE
);
466 unsigned int session_device_try_pause_all(Session
*s
) {
469 unsigned int num_pending
= 0;
473 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
475 session_device_notify(sd
, SESSION_DEVICE_TRY_PAUSE
);