Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <fcntl.h>
4 : #include <linux/input.h>
5 : #include <string.h>
6 : #include <sys/ioctl.h>
7 : #include <sys/types.h>
8 :
9 : #include "sd-device.h"
10 : #include "sd-daemon.h"
11 :
12 : #include "alloc-util.h"
13 : #include "bus-util.h"
14 : #include "fd-util.h"
15 : #include "logind-session-dbus.h"
16 : #include "logind-session-device.h"
17 : #include "missing.h"
18 : #include "parse-util.h"
19 : #include "util.h"
20 :
21 : enum SessionDeviceNotifications {
22 : SESSION_DEVICE_RESUME,
23 : SESSION_DEVICE_TRY_PAUSE,
24 : SESSION_DEVICE_PAUSE,
25 : SESSION_DEVICE_RELEASE,
26 : };
27 :
28 0 : static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
29 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
30 0 : _cleanup_free_ char *path = NULL;
31 0 : const char *t = NULL;
32 : uint32_t major, minor;
33 : int r;
34 :
35 0 : assert(sd);
36 :
37 0 : major = major(sd->dev);
38 0 : minor = minor(sd->dev);
39 :
40 0 : if (!sd->session->controller)
41 0 : return 0;
42 :
43 0 : path = session_bus_path(sd->session);
44 0 : if (!path)
45 0 : return -ENOMEM;
46 :
47 0 : r = sd_bus_message_new_signal(
48 0 : sd->session->manager->bus,
49 : &m, path,
50 : "org.freedesktop.login1.Session",
51 : (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
52 0 : if (!m)
53 0 : return r;
54 :
55 0 : r = sd_bus_message_set_destination(m, sd->session->controller);
56 0 : if (r < 0)
57 0 : return r;
58 :
59 0 : switch (type) {
60 :
61 0 : case SESSION_DEVICE_RESUME:
62 0 : r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
63 0 : if (r < 0)
64 0 : return r;
65 0 : break;
66 :
67 0 : case SESSION_DEVICE_TRY_PAUSE:
68 0 : t = "pause";
69 0 : break;
70 :
71 0 : case SESSION_DEVICE_PAUSE:
72 0 : t = "force";
73 0 : break;
74 :
75 0 : case SESSION_DEVICE_RELEASE:
76 0 : t = "gone";
77 0 : break;
78 :
79 0 : default:
80 0 : return -EINVAL;
81 : }
82 :
83 0 : if (t) {
84 0 : r = sd_bus_message_append(m, "uus", major, minor, t);
85 0 : if (r < 0)
86 0 : return r;
87 : }
88 :
89 0 : return sd_bus_send(sd->session->manager->bus, m, NULL);
90 : }
91 :
92 0 : static void sd_eviocrevoke(int fd) {
93 : static bool warned = false;
94 :
95 0 : assert(fd >= 0);
96 :
97 0 : if (ioctl(fd, EVIOCREVOKE, NULL) < 0) {
98 :
99 0 : if (errno == EINVAL && !warned) {
100 0 : log_warning_errno(errno, "Kernel does not support evdev-revocation: %m");
101 0 : warned = true;
102 : }
103 : }
104 0 : }
105 :
106 0 : static int sd_drmsetmaster(int fd) {
107 0 : assert(fd >= 0);
108 :
109 0 : if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0)
110 0 : return -errno;
111 :
112 0 : return 0;
113 : }
114 :
115 0 : static int sd_drmdropmaster(int fd) {
116 0 : assert(fd >= 0);
117 :
118 0 : if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) < 0)
119 0 : return -errno;
120 :
121 0 : return 0;
122 : }
123 :
124 0 : static int session_device_open(SessionDevice *sd, bool active) {
125 : int fd, r;
126 :
127 0 : assert(sd);
128 0 : assert(sd->type != DEVICE_TYPE_UNKNOWN);
129 0 : assert(sd->node);
130 :
131 : /* open device and try to get an udev_device from it */
132 0 : fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
133 0 : if (fd < 0)
134 0 : return -errno;
135 :
136 0 : switch (sd->type) {
137 :
138 0 : case DEVICE_TYPE_DRM:
139 0 : if (active) {
140 : /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
141 : * that so fail at all times and let caller retry in inactive state. */
142 0 : r = sd_drmsetmaster(fd);
143 0 : if (r < 0) {
144 0 : close_nointr(fd);
145 0 : return r;
146 : }
147 : } else
148 : /* DRM-Master is granted to the first user who opens a device automatically (ughh,
149 : * racy!). Hence, we just drop DRM-Master in case we were the first. */
150 0 : (void) sd_drmdropmaster(fd);
151 0 : break;
152 :
153 0 : case DEVICE_TYPE_EVDEV:
154 0 : if (!active)
155 0 : sd_eviocrevoke(fd);
156 0 : break;
157 :
158 0 : case DEVICE_TYPE_UNKNOWN:
159 : default:
160 : /* fallback for devices without synchronizations */
161 0 : break;
162 : }
163 :
164 0 : return fd;
165 : }
166 :
167 0 : static int session_device_start(SessionDevice *sd) {
168 : int r;
169 :
170 0 : assert(sd);
171 0 : assert(session_is_active(sd->session));
172 :
173 0 : if (sd->active)
174 0 : return 0;
175 :
176 0 : switch (sd->type) {
177 :
178 0 : case DEVICE_TYPE_DRM:
179 0 : if (sd->fd < 0)
180 0 : return log_error_errno(SYNTHETIC_ERRNO(EBADF),
181 : "Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
182 :
183 : /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
184 : * keep the device paused. Maybe at some point we have a drmStealMaster(). */
185 0 : r = sd_drmsetmaster(sd->fd);
186 0 : if (r < 0)
187 0 : return r;
188 0 : break;
189 :
190 0 : case DEVICE_TYPE_EVDEV:
191 : /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
192 0 : r = session_device_open(sd, true);
193 0 : if (r < 0)
194 0 : return r;
195 :
196 : /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
197 : * into a session and logind has been restarted right before. */
198 0 : safe_close(sd->fd);
199 0 : sd->fd = r;
200 0 : break;
201 :
202 0 : case DEVICE_TYPE_UNKNOWN:
203 : default:
204 : /* fallback for devices without synchronizations */
205 0 : break;
206 : }
207 :
208 0 : sd->active = true;
209 0 : return 0;
210 : }
211 :
212 0 : static void session_device_stop(SessionDevice *sd) {
213 0 : assert(sd);
214 :
215 0 : if (!sd->active)
216 0 : return;
217 :
218 0 : switch (sd->type) {
219 :
220 0 : case DEVICE_TYPE_DRM:
221 0 : if (sd->fd < 0) {
222 0 : log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
223 0 : return;
224 : }
225 :
226 : /* On DRM devices we simply drop DRM-Master but keep it open.
227 : * This allows the user to keep resources allocated. The
228 : * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
229 : * circumventing this. */
230 0 : sd_drmdropmaster(sd->fd);
231 0 : break;
232 :
233 0 : case DEVICE_TYPE_EVDEV:
234 : /* Revoke access on evdev file-descriptors during deactivation.
235 : * This will basically prevent any operations on the fd and
236 : * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
237 : * protection this way. */
238 0 : sd_eviocrevoke(sd->fd);
239 0 : break;
240 :
241 0 : case DEVICE_TYPE_UNKNOWN:
242 : default:
243 : /* fallback for devices without synchronization */
244 0 : break;
245 : }
246 :
247 0 : sd->active = false;
248 : }
249 :
250 0 : static DeviceType detect_device_type(sd_device *dev) {
251 : const char *sysname, *subsystem;
252 0 : DeviceType type = DEVICE_TYPE_UNKNOWN;
253 :
254 0 : if (sd_device_get_sysname(dev, &sysname) < 0 ||
255 0 : sd_device_get_subsystem(dev, &subsystem) < 0)
256 0 : return type;
257 :
258 0 : if (streq(subsystem, "drm")) {
259 0 : if (startswith(sysname, "card"))
260 0 : type = DEVICE_TYPE_DRM;
261 0 : } else if (streq(subsystem, "input")) {
262 0 : if (startswith(sysname, "event"))
263 0 : type = DEVICE_TYPE_EVDEV;
264 : }
265 :
266 0 : return type;
267 : }
268 :
269 0 : static int session_device_verify(SessionDevice *sd) {
270 0 : _cleanup_(sd_device_unrefp) sd_device *p = NULL;
271 : const char *sp, *node;
272 : sd_device *dev;
273 : int r;
274 :
275 0 : r = sd_device_new_from_devnum(&p, 'c', sd->dev);
276 0 : if (r < 0)
277 0 : return r;
278 :
279 0 : dev = p;
280 :
281 0 : if (sd_device_get_syspath(dev, &sp) < 0 ||
282 0 : sd_device_get_devname(dev, &node) < 0)
283 0 : return -EINVAL;
284 :
285 : /* detect device type so we can find the correct sysfs parent */
286 0 : sd->type = detect_device_type(dev);
287 0 : if (sd->type == DEVICE_TYPE_UNKNOWN)
288 0 : return -ENODEV;
289 :
290 0 : else if (sd->type == DEVICE_TYPE_EVDEV) {
291 : /* for evdev devices we need the parent node as device */
292 0 : if (sd_device_get_parent_with_subsystem_devtype(p, "input", NULL, &dev) < 0)
293 0 : return -ENODEV;
294 0 : if (sd_device_get_syspath(dev, &sp) < 0)
295 0 : return -ENODEV;
296 :
297 0 : } else if (sd->type != DEVICE_TYPE_DRM)
298 : /* Prevent opening unsupported devices. Especially devices of
299 : * subsystem "input" must be opened via the evdev node as
300 : * we require EVIOCREVOKE. */
301 0 : return -ENODEV;
302 :
303 : /* search for an existing seat device and return it if available */
304 0 : sd->device = hashmap_get(sd->session->manager->devices, sp);
305 0 : if (!sd->device) {
306 : /* The caller might have gotten the udev event before we were
307 : * able to process it. Hence, fake the "add" event and let the
308 : * logind-manager handle the new device. */
309 0 : r = manager_process_seat_device(sd->session->manager, dev);
310 0 : if (r < 0)
311 0 : return r;
312 :
313 : /* if it's still not available, then the device is invalid */
314 0 : sd->device = hashmap_get(sd->session->manager->devices, sp);
315 0 : if (!sd->device)
316 0 : return -ENODEV;
317 : }
318 :
319 0 : if (sd->device->seat != sd->session->seat)
320 0 : return -EPERM;
321 :
322 0 : sd->node = strdup(node);
323 0 : if (!sd->node)
324 0 : return -ENOMEM;
325 :
326 0 : return 0;
327 : }
328 :
329 0 : int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
330 : SessionDevice *sd;
331 : int r;
332 :
333 0 : assert(s);
334 0 : assert(out);
335 :
336 0 : if (!s->seat)
337 0 : return -EPERM;
338 :
339 0 : sd = new0(SessionDevice, 1);
340 0 : if (!sd)
341 0 : return -ENOMEM;
342 :
343 0 : sd->session = s;
344 0 : sd->dev = dev;
345 0 : sd->fd = -1;
346 0 : sd->type = DEVICE_TYPE_UNKNOWN;
347 :
348 0 : r = session_device_verify(sd);
349 0 : if (r < 0)
350 0 : goto error;
351 :
352 0 : r = hashmap_put(s->devices, &sd->dev, sd);
353 0 : if (r < 0)
354 0 : goto error;
355 :
356 0 : if (open_device) {
357 : /* Open the device for the first time. We need a valid fd to pass back
358 : * to the caller. If the session is not active, this _might_ immediately
359 : * revoke access and thus invalidate the fd. But this is still needed
360 : * to pass a valid fd back. */
361 0 : sd->active = session_is_active(s);
362 0 : r = session_device_open(sd, sd->active);
363 0 : if (r < 0) {
364 : /* EINVAL _may_ mean a master is active; retry inactive */
365 0 : if (sd->active && r == -EINVAL) {
366 0 : sd->active = false;
367 0 : r = session_device_open(sd, false);
368 : }
369 0 : if (r < 0)
370 0 : goto error;
371 : }
372 0 : sd->fd = r;
373 : }
374 :
375 0 : LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
376 :
377 0 : *out = sd;
378 0 : return 0;
379 :
380 0 : error:
381 0 : hashmap_remove(s->devices, &sd->dev);
382 0 : free(sd->node);
383 0 : free(sd);
384 0 : return r;
385 : }
386 :
387 0 : void session_device_free(SessionDevice *sd) {
388 0 : assert(sd);
389 :
390 : /* Make sure to remove the pushed fd. */
391 0 : if (sd->pushed_fd)
392 0 : (void) sd_notifyf(false,
393 : "FDSTOREREMOVE=1\n"
394 : "FDNAME=session-%s-device-%u-%u",
395 0 : sd->session->id, major(sd->dev), minor(sd->dev));
396 :
397 0 : session_device_stop(sd);
398 0 : session_device_notify(sd, SESSION_DEVICE_RELEASE);
399 0 : safe_close(sd->fd);
400 :
401 0 : LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
402 :
403 0 : hashmap_remove(sd->session->devices, &sd->dev);
404 :
405 0 : free(sd->node);
406 0 : free(sd);
407 0 : }
408 :
409 0 : void session_device_complete_pause(SessionDevice *sd) {
410 : SessionDevice *iter;
411 : Iterator i;
412 :
413 0 : if (!sd->active)
414 0 : return;
415 :
416 0 : session_device_stop(sd);
417 :
418 : /* if not all devices are paused, wait for further completion events */
419 0 : HASHMAP_FOREACH(iter, sd->session->devices, i)
420 0 : if (iter->active)
421 0 : return;
422 :
423 : /* complete any pending session switch */
424 0 : seat_complete_switch(sd->session->seat);
425 : }
426 :
427 0 : void session_device_resume_all(Session *s) {
428 : SessionDevice *sd;
429 : Iterator i;
430 :
431 0 : assert(s);
432 :
433 0 : HASHMAP_FOREACH(sd, s->devices, i) {
434 0 : if (sd->active)
435 0 : continue;
436 :
437 0 : if (session_device_start(sd) < 0)
438 0 : continue;
439 0 : if (session_device_save(sd) < 0)
440 0 : continue;
441 :
442 0 : session_device_notify(sd, SESSION_DEVICE_RESUME);
443 : }
444 0 : }
445 :
446 0 : void session_device_pause_all(Session *s) {
447 : SessionDevice *sd;
448 : Iterator i;
449 :
450 0 : assert(s);
451 :
452 0 : HASHMAP_FOREACH(sd, s->devices, i) {
453 0 : if (!sd->active)
454 0 : continue;
455 :
456 0 : session_device_stop(sd);
457 0 : session_device_notify(sd, SESSION_DEVICE_PAUSE);
458 : }
459 0 : }
460 :
461 0 : unsigned session_device_try_pause_all(Session *s) {
462 0 : unsigned num_pending = 0;
463 : SessionDevice *sd;
464 : Iterator i;
465 :
466 0 : assert(s);
467 :
468 0 : HASHMAP_FOREACH(sd, s->devices, i) {
469 0 : if (!sd->active)
470 0 : continue;
471 :
472 0 : session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
473 0 : num_pending++;
474 : }
475 :
476 0 : return num_pending;
477 : }
478 :
479 0 : int session_device_save(SessionDevice *sd) {
480 0 : _cleanup_free_ char *m = NULL;
481 : const char *id;
482 : int r;
483 :
484 0 : assert(sd);
485 :
486 : /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
487 : * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
488 : * don't have to handle them differently later.
489 : *
490 : * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
491 : * is revoked. */
492 :
493 0 : if (sd->pushed_fd)
494 0 : return 0;
495 :
496 : /* Session ID does not contain separators. */
497 0 : id = sd->session->id;
498 0 : assert(*(id + strcspn(id, "-\n")) == '\0');
499 :
500 0 : r = asprintf(&m, "FDSTORE=1\n"
501 : "FDNAME=session-%s-device-%u-%u\n",
502 : id, major(sd->dev), minor(sd->dev));
503 0 : if (r < 0)
504 0 : return r;
505 :
506 0 : r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
507 0 : if (r < 0)
508 0 : return r;
509 :
510 0 : sd->pushed_fd = true;
511 0 : return 1;
512 : }
513 :
514 0 : void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
515 0 : assert(fd >= 0);
516 0 : assert(sd);
517 0 : assert(sd->fd < 0);
518 0 : assert(!sd->active);
519 :
520 0 : sd->fd = fd;
521 0 : sd->pushed_fd = true;
522 0 : sd->active = active;
523 0 : }
|