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