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