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