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