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/>.
25 #include <linux/input.h>
26 #include <linux/ioctl.h>
28 #include <sys/ioctl.h>
30 #include <sys/types.h>
33 #include "dbus-common.h"
34 #include "logind-session-device.h"
38 enum SessionDeviceNotifications {
39 SESSION_DEVICE_RESUME,
40 SESSION_DEVICE_TRY_PAUSE,
42 SESSION_DEVICE_RELEASE,
45 static void session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
46 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
47 _cleanup_free_ char *path = NULL;
52 if (!sd->session->controller)
55 path = session_bus_path(sd->session);
59 m = dbus_message_new_signal(path,
60 "org.freedesktop.login1.Session",
61 (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
65 if (!dbus_message_set_destination(m, sd->session->controller))
69 case SESSION_DEVICE_RESUME:
70 if (!dbus_message_append_args(m,
71 DBUS_TYPE_UINT32, major(sd->dev),
72 DBUS_TYPE_UINT32, minor(sd->dev),
73 DBUS_TYPE_UNIX_FD, &sd->fd,
77 case SESSION_DEVICE_TRY_PAUSE:
80 case SESSION_DEVICE_PAUSE:
83 case SESSION_DEVICE_RELEASE:
90 if (t && !dbus_message_append_args(m,
91 DBUS_TYPE_UINT32, major(sd->dev),
92 DBUS_TYPE_UINT32, minor(sd->dev),
97 dbus_connection_send(sd->session->manager->bus, m, NULL);
100 static int sd_eviocrevoke(int fd) {
106 r = ioctl(fd, EVIOCREVOKE, 1);
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:
157 /* DRM-Master is granted to the first user who opens a
158 * device automatically (ughh, racy!). Hence, we just
159 * drop DRM-Master in case we were the first. */
160 sd_drmdropmaster(fd);
163 case DEVICE_TYPE_EVDEV:
167 case DEVICE_TYPE_FBDEV:
168 case DEVICE_TYPE_UNKNOWN:
170 /* fallback for devices wihout synchronizations */
177 static int session_device_start(SessionDevice *sd) {
181 assert(session_is_active(sd->session));
187 case DEVICE_TYPE_DRM:
188 /* Device is kept open. Simply call drmSetMaster() and hope
189 * there is no-one else. In case it fails, we keep the device
190 * paused. Maybe at some point we have a drmStealMaster(). */
191 r = sd_drmsetmaster(sd->fd);
195 case DEVICE_TYPE_EVDEV:
196 /* Evdev devices are revoked while inactive. Reopen it and we
198 r = session_device_open(sd, true);
201 close_nointr_nofail(sd->fd);
204 case DEVICE_TYPE_FBDEV:
205 /* fbdev devices have no way to synchronize access. Moreover,
206 * they mostly operate through mmaps() without any pageflips
207 * and modesetting, so there is no way for us to prevent access
208 * but tear down mmaps.
209 * That would be quite expensive to do on a per-fd context. So
210 * ignore legcy fbdev and let its users feel the pain they asked
211 * for when deciding for fbdev. */
212 case DEVICE_TYPE_UNKNOWN:
214 /* fallback for devices wihout synchronizations */
222 static void session_device_stop(SessionDevice *sd) {
229 case DEVICE_TYPE_DRM:
230 /* On DRM devices we simply drop DRM-Master but keep it open.
231 * This allows the user to keep resources allocated. The
232 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
233 * circumventing this. */
234 sd_drmdropmaster(sd->fd);
236 case DEVICE_TYPE_EVDEV:
237 /* Revoke access on evdev file-descriptors during deactivation.
238 * This will basically prevent any operations on the fd and
239 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
240 * protection this way. */
241 sd_eviocrevoke(sd->fd);
243 case DEVICE_TYPE_FBDEV:
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, "graphics")) {
262 if (!streq(sysname, "fbcon") && startswith(sysname, "fb"))
263 type = DEVICE_TYPE_FBDEV;
264 } else if (streq_ptr(subsystem, "drm")) {
265 if (startswith(sysname, "card"))
266 type = DEVICE_TYPE_DRM;
267 } else if (streq_ptr(subsystem, "input")) {
268 if (startswith(sysname, "event"))
269 type = DEVICE_TYPE_EVDEV;
275 static int session_device_verify(SessionDevice *sd) {
276 struct udev_device *dev, *p = NULL;
277 const char *sp, *node;
280 dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
284 sp = udev_device_get_syspath(dev);
285 node = udev_device_get_devnode(dev);
291 /* detect device type so we can find the correct sysfs parent */
292 sd->type = detect_device_type(dev);
293 if (sd->type == DEVICE_TYPE_UNKNOWN) {
296 } else if (sd->type == DEVICE_TYPE_EVDEV) {
297 /* for evdev devices we need the parent node as device */
299 dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
304 sp = udev_device_get_syspath(dev);
305 } else if (sd->type != DEVICE_TYPE_FBDEV &&
306 sd->type != DEVICE_TYPE_DRM) {
307 /* Prevent opening unsupported devices. Especially devices of
308 * subsystem "input" must be opened via the evdev node as
309 * we require EVIOCREVOKE. */
314 /* search for an existing seat device and return it if available */
315 sd->device = hashmap_get(sd->session->manager->devices, sp);
317 /* The caller might have gotten the udev event before we were
318 * able to process it. Hence, fake the "add" event and let the
319 * logind-manager handle the new device. */
320 r = manager_process_seat_device(sd->session->manager, dev);
324 /* if it's still not available, then the device is invalid */
325 sd->device = hashmap_get(sd->session->manager->devices, sp);
332 if (sd->device->seat != sd->session->seat) {
337 sd->node = strdup(node);
345 udev_device_unref(p ? : dev);
349 int session_device_new(Session *s, dev_t dev, SessionDevice **out) {
359 sd = new0(SessionDevice, 1);
366 sd->type = DEVICE_TYPE_UNKNOWN;
368 r = session_device_verify(sd);
372 r = hashmap_put(s->devices, &sd->dev, sd);
378 /* Open the device for the first time. We need a valid fd to pass back
379 * to the caller. If the session is not active, this _might_ immediately
380 * revoke access and thus invalidate the fd. But this is still needed
381 * to pass a valid fd back. */
382 sd->active = session_is_active(s);
383 sd->fd = session_device_open(sd, sd->active);
387 LIST_PREPEND(SessionDevice, sd_by_device, sd->device->session_devices, sd);
393 hashmap_remove(s->devices, &sd->dev);
399 void session_device_free(SessionDevice *sd) {
402 session_device_stop(sd);
403 session_device_notify(sd, SESSION_DEVICE_RELEASE);
404 close_nointr_nofail(sd->fd);
406 LIST_REMOVE(SessionDevice, sd_by_device, sd->device->session_devices, sd);
408 hashmap_remove(sd->session->devices, &sd->dev);
414 void session_device_complete_pause(SessionDevice *sd) {
421 session_device_stop(sd);
423 /* if not all devices are paused, wait for further completion events */
424 HASHMAP_FOREACH(iter, sd->session->devices, i)
428 /* complete any pending session switch */
429 seat_complete_switch(sd->session->seat);
432 void session_device_resume_all(Session *s) {
439 HASHMAP_FOREACH(sd, s->devices, i) {
441 r = session_device_start(sd);
443 session_device_notify(sd, SESSION_DEVICE_RESUME);
448 void session_device_pause_all(Session *s) {
454 HASHMAP_FOREACH(sd, s->devices, i) {
456 session_device_stop(sd);
457 session_device_notify(sd, SESSION_DEVICE_PAUSE);
462 unsigned int session_device_try_pause_all(Session *s) {
465 unsigned int num_pending = 0;
469 HASHMAP_FOREACH(sd, s->devices, i) {
471 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);