Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2013 David Herrmann
7 :
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.
12 :
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.
17 :
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/>.
20 : ***/
21 :
22 : #include <fcntl.h>
23 : #include <libudev.h>
24 : #include <linux/input.h>
25 : #include <string.h>
26 : #include <sys/ioctl.h>
27 : #include <sys/types.h>
28 :
29 : #include "util.h"
30 : #include "missing.h"
31 : #include "bus-util.h"
32 : #include "logind-session-device.h"
33 :
34 : enum SessionDeviceNotifications {
35 : SESSION_DEVICE_RESUME,
36 : SESSION_DEVICE_TRY_PAUSE,
37 : SESSION_DEVICE_PAUSE,
38 : SESSION_DEVICE_RELEASE,
39 : };
40 :
41 0 : static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
42 0 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
43 0 : _cleanup_free_ char *path = NULL;
44 0 : const char *t = NULL;
45 : uint32_t major, minor;
46 : int r;
47 :
48 0 : assert(sd);
49 :
50 0 : major = major(sd->dev);
51 0 : minor = minor(sd->dev);
52 :
53 0 : if (!sd->session->controller)
54 0 : return 0;
55 :
56 0 : path = session_bus_path(sd->session);
57 0 : if (!path)
58 0 : return -ENOMEM;
59 :
60 0 : r = sd_bus_message_new_signal(
61 0 : sd->session->manager->bus,
62 : &m, path,
63 : "org.freedesktop.login1.Session",
64 : (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
65 0 : if (!m)
66 0 : return r;
67 :
68 0 : r = sd_bus_message_set_destination(m, sd->session->controller);
69 0 : if (r < 0)
70 0 : return r;
71 :
72 0 : switch (type) {
73 : case SESSION_DEVICE_RESUME:
74 0 : r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
75 0 : if (r < 0)
76 0 : return r;
77 0 : break;
78 : case SESSION_DEVICE_TRY_PAUSE:
79 0 : t = "pause";
80 0 : break;
81 : case SESSION_DEVICE_PAUSE:
82 0 : t = "force";
83 0 : break;
84 : case SESSION_DEVICE_RELEASE:
85 0 : t = "gone";
86 0 : break;
87 : default:
88 0 : return -EINVAL;
89 : }
90 :
91 0 : if (t) {
92 0 : r = sd_bus_message_append(m, "uus", major, minor, t);
93 0 : if (r < 0)
94 0 : return r;
95 : }
96 :
97 0 : return sd_bus_send(sd->session->manager->bus, m, NULL);
98 : }
99 :
100 0 : static int sd_eviocrevoke(int fd) {
101 : static bool warned;
102 : int r;
103 :
104 0 : assert(fd >= 0);
105 :
106 0 : r = ioctl(fd, EVIOCREVOKE, NULL);
107 0 : if (r < 0) {
108 0 : r = -errno;
109 0 : if (r == -EINVAL && !warned) {
110 0 : warned = true;
111 0 : log_warning("kernel does not support evdev-revocation");
112 : }
113 : }
114 :
115 0 : return 0;
116 : }
117 :
118 0 : static int sd_drmsetmaster(int fd) {
119 : int r;
120 :
121 0 : assert(fd >= 0);
122 :
123 0 : r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
124 0 : if (r < 0)
125 0 : return -errno;
126 :
127 0 : return 0;
128 : }
129 :
130 0 : static int sd_drmdropmaster(int fd) {
131 : int r;
132 :
133 0 : assert(fd >= 0);
134 :
135 0 : r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
136 0 : if (r < 0)
137 0 : return -errno;
138 :
139 0 : return 0;
140 : }
141 :
142 0 : static int session_device_open(SessionDevice *sd, bool active) {
143 : int fd, r;
144 :
145 0 : assert(sd->type != DEVICE_TYPE_UNKNOWN);
146 :
147 : /* open device and try to get an udev_device from it */
148 0 : fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
149 0 : if (fd < 0)
150 0 : return -errno;
151 :
152 0 : switch (sd->type) {
153 : case DEVICE_TYPE_DRM:
154 0 : if (active) {
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
158 : * state. */
159 0 : r = sd_drmsetmaster(fd);
160 0 : if (r < 0) {
161 0 : close_nointr(fd);
162 0 : return r;
163 : }
164 : } else {
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 0 : sd_drmdropmaster(fd);
169 : }
170 0 : break;
171 : case DEVICE_TYPE_EVDEV:
172 0 : if (!active)
173 0 : sd_eviocrevoke(fd);
174 0 : break;
175 : case DEVICE_TYPE_UNKNOWN:
176 : default:
177 : /* fallback for devices wihout synchronizations */
178 0 : break;
179 : }
180 :
181 0 : return fd;
182 : }
183 :
184 0 : static int session_device_start(SessionDevice *sd) {
185 : int r;
186 :
187 0 : assert(sd);
188 0 : assert(session_is_active(sd->session));
189 :
190 0 : if (sd->active)
191 0 : return 0;
192 :
193 0 : switch (sd->type) {
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 0 : r = sd_drmsetmaster(sd->fd);
199 0 : if (r < 0)
200 0 : return r;
201 0 : break;
202 : case DEVICE_TYPE_EVDEV:
203 : /* Evdev devices are revoked while inactive. Reopen it and we
204 : * are fine. */
205 0 : r = session_device_open(sd, true);
206 0 : if (r < 0)
207 0 : return r;
208 0 : close_nointr(sd->fd);
209 0 : sd->fd = r;
210 0 : break;
211 : case DEVICE_TYPE_UNKNOWN:
212 : default:
213 : /* fallback for devices wihout synchronizations */
214 0 : break;
215 : }
216 :
217 0 : sd->active = true;
218 0 : return 0;
219 : }
220 :
221 0 : static void session_device_stop(SessionDevice *sd) {
222 0 : assert(sd);
223 :
224 0 : if (!sd->active)
225 0 : return;
226 :
227 0 : switch (sd->type) {
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 0 : sd_drmdropmaster(sd->fd);
234 0 : break;
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 0 : sd_eviocrevoke(sd->fd);
241 0 : break;
242 : case DEVICE_TYPE_UNKNOWN:
243 : default:
244 : /* fallback for devices without synchronization */
245 0 : break;
246 : }
247 :
248 0 : sd->active = false;
249 : }
250 :
251 0 : static DeviceType detect_device_type(struct udev_device *dev) {
252 : const char *sysname, *subsystem;
253 : DeviceType type;
254 :
255 0 : sysname = udev_device_get_sysname(dev);
256 0 : subsystem = udev_device_get_subsystem(dev);
257 0 : type = DEVICE_TYPE_UNKNOWN;
258 :
259 0 : if (streq_ptr(subsystem, "drm")) {
260 0 : if (startswith(sysname, "card"))
261 0 : type = DEVICE_TYPE_DRM;
262 0 : } else if (streq_ptr(subsystem, "input")) {
263 0 : if (startswith(sysname, "event"))
264 0 : type = DEVICE_TYPE_EVDEV;
265 : }
266 :
267 0 : return type;
268 : }
269 :
270 0 : static int session_device_verify(SessionDevice *sd) {
271 0 : struct udev_device *dev, *p = NULL;
272 : const char *sp, *node;
273 : int r;
274 :
275 0 : dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
276 0 : if (!dev)
277 0 : return -ENODEV;
278 :
279 0 : sp = udev_device_get_syspath(dev);
280 0 : node = udev_device_get_devnode(dev);
281 0 : if (!node) {
282 0 : r = -EINVAL;
283 0 : goto err_dev;
284 : }
285 :
286 : /* detect device type so we can find the correct sysfs parent */
287 0 : sd->type = detect_device_type(dev);
288 0 : if (sd->type == DEVICE_TYPE_UNKNOWN) {
289 0 : r = -ENODEV;
290 0 : goto err_dev;
291 0 : } else if (sd->type == DEVICE_TYPE_EVDEV) {
292 : /* for evdev devices we need the parent node as device */
293 0 : p = dev;
294 0 : dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
295 0 : if (!dev) {
296 0 : r = -ENODEV;
297 0 : goto err_dev;
298 : }
299 0 : sp = udev_device_get_syspath(dev);
300 0 : } 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. */
304 0 : r = -ENODEV;
305 0 : goto err_dev;
306 : }
307 :
308 : /* search for an existing seat device and return it if available */
309 0 : sd->device = hashmap_get(sd->session->manager->devices, sp);
310 0 : if (!sd->device) {
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 0 : r = manager_process_seat_device(sd->session->manager, dev);
315 0 : if (r < 0)
316 0 : goto err_dev;
317 :
318 : /* if it's still not available, then the device is invalid */
319 0 : sd->device = hashmap_get(sd->session->manager->devices, sp);
320 0 : if (!sd->device) {
321 0 : r = -ENODEV;
322 0 : goto err_dev;
323 : }
324 : }
325 :
326 0 : if (sd->device->seat != sd->session->seat) {
327 0 : r = -EPERM;
328 0 : goto err_dev;
329 : }
330 :
331 0 : sd->node = strdup(node);
332 0 : if (!sd->node) {
333 0 : r = -ENOMEM;
334 0 : goto err_dev;
335 : }
336 :
337 0 : r = 0;
338 : err_dev:
339 0 : udev_device_unref(p ? : dev);
340 0 : return r;
341 : }
342 :
343 0 : int session_device_new(Session *s, dev_t dev, SessionDevice **out) {
344 : SessionDevice *sd;
345 : int r;
346 :
347 0 : assert(s);
348 0 : assert(out);
349 :
350 0 : if (!s->seat)
351 0 : return -EPERM;
352 :
353 0 : sd = new0(SessionDevice, 1);
354 0 : if (!sd)
355 0 : return -ENOMEM;
356 :
357 0 : sd->session = s;
358 0 : sd->dev = dev;
359 0 : sd->fd = -1;
360 0 : sd->type = DEVICE_TYPE_UNKNOWN;
361 :
362 0 : r = session_device_verify(sd);
363 0 : if (r < 0)
364 0 : goto error;
365 :
366 0 : r = hashmap_put(s->devices, &sd->dev, sd);
367 0 : if (r < 0) {
368 0 : r = -ENOMEM;
369 0 : goto error;
370 : }
371 :
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 0 : sd->active = session_is_active(s);
377 0 : r = session_device_open(sd, sd->active);
378 0 : if (r < 0) {
379 : /* EINVAL _may_ mean a master is active; retry inactive */
380 0 : if (sd->active && r == -EINVAL) {
381 0 : sd->active = false;
382 0 : r = session_device_open(sd, false);
383 : }
384 0 : if (r < 0)
385 0 : goto error;
386 : }
387 0 : sd->fd = r;
388 :
389 0 : LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
390 :
391 0 : *out = sd;
392 0 : return 0;
393 :
394 : error:
395 0 : hashmap_remove(s->devices, &sd->dev);
396 0 : free(sd->node);
397 0 : free(sd);
398 0 : return r;
399 : }
400 :
401 0 : void session_device_free(SessionDevice *sd) {
402 0 : assert(sd);
403 :
404 0 : session_device_stop(sd);
405 0 : session_device_notify(sd, SESSION_DEVICE_RELEASE);
406 0 : close_nointr(sd->fd);
407 :
408 0 : LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
409 :
410 0 : hashmap_remove(sd->session->devices, &sd->dev);
411 :
412 0 : free(sd->node);
413 0 : free(sd);
414 0 : }
415 :
416 0 : void session_device_complete_pause(SessionDevice *sd) {
417 : SessionDevice *iter;
418 : Iterator i;
419 :
420 0 : if (!sd->active)
421 0 : return;
422 :
423 0 : session_device_stop(sd);
424 :
425 : /* if not all devices are paused, wait for further completion events */
426 0 : HASHMAP_FOREACH(iter, sd->session->devices, i)
427 0 : if (iter->active)
428 0 : return;
429 :
430 : /* complete any pending session switch */
431 0 : seat_complete_switch(sd->session->seat);
432 : }
433 :
434 0 : void session_device_resume_all(Session *s) {
435 : SessionDevice *sd;
436 : Iterator i;
437 : int r;
438 :
439 0 : assert(s);
440 :
441 0 : HASHMAP_FOREACH(sd, s->devices, i) {
442 0 : if (!sd->active) {
443 0 : r = session_device_start(sd);
444 0 : if (!r)
445 0 : session_device_notify(sd, SESSION_DEVICE_RESUME);
446 : }
447 : }
448 0 : }
449 :
450 0 : void session_device_pause_all(Session *s) {
451 : SessionDevice *sd;
452 : Iterator i;
453 :
454 0 : assert(s);
455 :
456 0 : HASHMAP_FOREACH(sd, s->devices, i) {
457 0 : if (sd->active) {
458 0 : session_device_stop(sd);
459 0 : session_device_notify(sd, SESSION_DEVICE_PAUSE);
460 : }
461 : }
462 0 : }
463 :
464 0 : unsigned int session_device_try_pause_all(Session *s) {
465 : SessionDevice *sd;
466 : Iterator i;
467 0 : unsigned int num_pending = 0;
468 :
469 0 : assert(s);
470 :
471 0 : HASHMAP_FOREACH(sd, s->devices, i) {
472 0 : if (sd->active) {
473 0 : session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
474 0 : ++num_pending;
475 : }
476 : }
477 :
478 0 : return num_pending;
479 : }
|