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/>.
24 #include <linux/input.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
32 #include "logind-session-device.h"
34 enum SessionDeviceNotifications
{
35 SESSION_DEVICE_RESUME
,
36 SESSION_DEVICE_TRY_PAUSE
,
38 SESSION_DEVICE_RELEASE
,
41 static int session_device_notify(SessionDevice
*sd
, enum SessionDeviceNotifications type
) {
42 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
43 _cleanup_free_
char *path
= NULL
;
45 uint32_t major
, minor
;
50 major
= major(sd
->dev
);
51 minor
= minor(sd
->dev
);
53 if (!sd
->session
->controller
)
56 path
= session_bus_path(sd
->session
);
60 r
= sd_bus_message_new_signal(
61 sd
->session
->manager
->bus
,
63 "org.freedesktop.login1.Session",
64 (type
== SESSION_DEVICE_RESUME
) ? "ResumeDevice" : "PauseDevice");
68 r
= sd_bus_message_set_destination(m
, sd
->session
->controller
);
73 case SESSION_DEVICE_RESUME
:
74 r
= sd_bus_message_append(m
, "uuh", major
, minor
, sd
->fd
);
78 case SESSION_DEVICE_TRY_PAUSE
:
81 case SESSION_DEVICE_PAUSE
:
84 case SESSION_DEVICE_RELEASE
:
92 r
= sd_bus_message_append(m
, "uus", major
, minor
, t
);
97 return sd_bus_send(sd
->session
->manager
->bus
, m
, NULL
);
100 static int sd_eviocrevoke(int fd
) {
106 r
= ioctl(fd
, EVIOCREVOKE
, NULL
);
109 if (r
== -EINVAL
&& !warned
) {
111 log_warning("kernel does not support evdev-revocation");
118 static int sd_drmsetmaster(int fd
) {
123 r
= ioctl(fd
, DRM_IOCTL_SET_MASTER
, 0);
130 static int sd_drmdropmaster(int fd
) {
135 r
= ioctl(fd
, DRM_IOCTL_DROP_MASTER
, 0);
142 static int session_device_open(SessionDevice
*sd
, bool active
) {
145 assert(sd
->type
!= DEVICE_TYPE_UNKNOWN
);
147 /* open device and try to get an udev_device from it */
148 fd
= open(sd
->node
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
|O_NONBLOCK
);
153 case DEVICE_TYPE_DRM
:
155 /* Weird legacy DRM semantics might return an error
156 * even though we're master. No way to detect that so
157 * fail at all times and let caller retry in inactive
159 r
= sd_drmsetmaster(fd
);
165 /* DRM-Master is granted to the first user who opens a
166 * device automatically (ughh, racy!). Hence, we just
167 * drop DRM-Master in case we were the first. */
168 sd_drmdropmaster(fd
);
171 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
));
194 case DEVICE_TYPE_DRM
:
195 /* Device is kept open. Simply call drmSetMaster() and hope
196 * there is no-one else. In case it fails, we keep the device
197 * paused. Maybe at some point we have a drmStealMaster(). */
198 r
= sd_drmsetmaster(sd
->fd
);
202 case DEVICE_TYPE_EVDEV
:
203 /* Evdev devices are revoked while inactive. Reopen it and we
205 r
= session_device_open(sd
, true);
208 close_nointr(sd
->fd
);
211 case DEVICE_TYPE_UNKNOWN
:
213 /* fallback for devices wihout synchronizations */
221 static void session_device_stop(SessionDevice
*sd
) {
228 case DEVICE_TYPE_DRM
:
229 /* On DRM devices we simply drop DRM-Master but keep it open.
230 * This allows the user to keep resources allocated. The
231 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
232 * circumventing this. */
233 sd_drmdropmaster(sd
->fd
);
235 case DEVICE_TYPE_EVDEV
:
236 /* Revoke access on evdev file-descriptors during deactivation.
237 * This will basically prevent any operations on the fd and
238 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
239 * protection this way. */
240 sd_eviocrevoke(sd
->fd
);
242 case DEVICE_TYPE_UNKNOWN
:
244 /* fallback for devices without synchronization */
251 static DeviceType
detect_device_type(struct udev_device
*dev
) {
252 const char *sysname
, *subsystem
;
255 sysname
= udev_device_get_sysname(dev
);
256 subsystem
= udev_device_get_subsystem(dev
);
257 type
= DEVICE_TYPE_UNKNOWN
;
259 if (streq_ptr(subsystem
, "drm")) {
260 if (startswith(sysname
, "card"))
261 type
= DEVICE_TYPE_DRM
;
262 } else if (streq_ptr(subsystem
, "input")) {
263 if (startswith(sysname
, "event"))
264 type
= DEVICE_TYPE_EVDEV
;
270 static int session_device_verify(SessionDevice
*sd
) {
271 struct udev_device
*dev
, *p
= NULL
;
272 const char *sp
, *node
;
275 dev
= udev_device_new_from_devnum(sd
->session
->manager
->udev
, 'c', sd
->dev
);
279 sp
= udev_device_get_syspath(dev
);
280 node
= udev_device_get_devnode(dev
);
286 /* detect device type so we can find the correct sysfs parent */
287 sd
->type
= detect_device_type(dev
);
288 if (sd
->type
== DEVICE_TYPE_UNKNOWN
) {
291 } else if (sd
->type
== DEVICE_TYPE_EVDEV
) {
292 /* for evdev devices we need the parent node as device */
294 dev
= udev_device_get_parent_with_subsystem_devtype(p
, "input", NULL
);
299 sp
= udev_device_get_syspath(dev
);
300 } else if (sd
->type
!= DEVICE_TYPE_DRM
) {
301 /* Prevent opening unsupported devices. Especially devices of
302 * subsystem "input" must be opened via the evdev node as
303 * we require EVIOCREVOKE. */
308 /* search for an existing seat device and return it if available */
309 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
311 /* The caller might have gotten the udev event before we were
312 * able to process it. Hence, fake the "add" event and let the
313 * logind-manager handle the new device. */
314 r
= manager_process_seat_device(sd
->session
->manager
, dev
);
318 /* if it's still not available, then the device is invalid */
319 sd
->device
= hashmap_get(sd
->session
->manager
->devices
, sp
);
326 if (sd
->device
->seat
!= sd
->session
->seat
) {
331 sd
->node
= strdup(node
);
339 udev_device_unref(p
? : dev
);
343 int session_device_new(Session
*s
, dev_t dev
, SessionDevice
**out
) {
353 sd
= new0(SessionDevice
, 1);
360 sd
->type
= DEVICE_TYPE_UNKNOWN
;
362 r
= session_device_verify(sd
);
366 r
= hashmap_put(s
->devices
, &sd
->dev
, sd
);
372 /* Open the device for the first time. We need a valid fd to pass back
373 * to the caller. If the session is not active, this _might_ immediately
374 * revoke access and thus invalidate the fd. But this is still needed
375 * to pass a valid fd back. */
376 sd
->active
= session_is_active(s
);
377 r
= session_device_open(sd
, sd
->active
);
379 /* EINVAL _may_ mean a master is active; retry inactive */
380 if (sd
->active
&& r
== -EINVAL
) {
382 r
= session_device_open(sd
, false);
389 LIST_PREPEND(sd_by_device
, sd
->device
->session_devices
, sd
);
395 hashmap_remove(s
->devices
, &sd
->dev
);
401 void session_device_free(SessionDevice
*sd
) {
404 session_device_stop(sd
);
405 session_device_notify(sd
, SESSION_DEVICE_RELEASE
);
406 close_nointr(sd
->fd
);
408 LIST_REMOVE(sd_by_device
, sd
->device
->session_devices
, sd
);
410 hashmap_remove(sd
->session
->devices
, &sd
->dev
);
416 void session_device_complete_pause(SessionDevice
*sd
) {
423 session_device_stop(sd
);
425 /* if not all devices are paused, wait for further completion events */
426 HASHMAP_FOREACH(iter
, sd
->session
->devices
, i
)
430 /* complete any pending session switch */
431 seat_complete_switch(sd
->session
->seat
);
434 void session_device_resume_all(Session
*s
) {
441 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
443 r
= session_device_start(sd
);
445 session_device_notify(sd
, SESSION_DEVICE_RESUME
);
450 void session_device_pause_all(Session
*s
) {
456 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
458 session_device_stop(sd
);
459 session_device_notify(sd
, SESSION_DEVICE_PAUSE
);
464 unsigned int session_device_try_pause_all(Session
*s
) {
467 unsigned int num_pending
= 0;
471 HASHMAP_FOREACH(sd
, s
->devices
, i
) {
473 session_device_notify(sd
, SESSION_DEVICE_TRY_PAUSE
);