chiark / gitweb /
logind: cast away return value we don't care about
[elogind.git] / src / login / logind-session-device.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2013 David Herrmann
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <linux/input.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26
27 #if 0 /// elogind needs the systems udev header
28 #include "libudev.h"
29 #else
30 #include <libudev.h>
31 #endif // 0
32
33 #include "alloc-util.h"
34 #include "bus-util.h"
35 #include "fd-util.h"
36 #include "logind-session-device.h"
37 #include "missing.h"
38 #include "parse-util.h"
39 #include "sd-daemon.h"
40 #include "util.h"
41
42 enum SessionDeviceNotifications {
43         SESSION_DEVICE_RESUME,
44         SESSION_DEVICE_TRY_PAUSE,
45         SESSION_DEVICE_PAUSE,
46         SESSION_DEVICE_RELEASE,
47 };
48
49 static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
50         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
51         _cleanup_free_ char *path = NULL;
52         const char *t = NULL;
53         uint32_t major, minor;
54         int r;
55
56         assert(sd);
57
58         major = major(sd->dev);
59         minor = minor(sd->dev);
60
61         if (!sd->session->controller)
62                 return 0;
63
64         path = session_bus_path(sd->session);
65         if (!path)
66                 return -ENOMEM;
67
68         r = sd_bus_message_new_signal(
69                         sd->session->manager->bus,
70                         &m, path,
71                         "org.freedesktop.login1.Session",
72                         (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
73         if (!m)
74                 return r;
75
76         r = sd_bus_message_set_destination(m, sd->session->controller);
77         if (r < 0)
78                 return r;
79
80         switch (type) {
81
82         case SESSION_DEVICE_RESUME:
83                 r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
84                 if (r < 0)
85                         return r;
86                 break;
87
88         case SESSION_DEVICE_TRY_PAUSE:
89                 t = "pause";
90                 break;
91
92         case SESSION_DEVICE_PAUSE:
93                 t = "force";
94                 break;
95
96         case SESSION_DEVICE_RELEASE:
97                 t = "gone";
98                 break;
99
100         default:
101                 return -EINVAL;
102         }
103
104         if (t) {
105                 r = sd_bus_message_append(m, "uus", major, minor, t);
106                 if (r < 0)
107                         return r;
108         }
109
110         return sd_bus_send(sd->session->manager->bus, m, NULL);
111 }
112
113 static void sd_eviocrevoke(int fd) {
114         static bool warned = false;
115
116         assert(fd >= 0);
117
118         if (ioctl(fd, EVIOCREVOKE, NULL) < 0) {
119
120                 if (errno == EINVAL && !warned) {
121                         log_warning_errno(errno, "Kernel does not support evdev-revocation: %m");
122                         warned = true;
123                 }
124         }
125 }
126
127 static int sd_drmsetmaster(int fd) {
128         assert(fd >= 0);
129
130         if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0)
131                 return -errno;
132
133         return 0;
134 }
135
136 static int sd_drmdropmaster(int fd) {
137         assert(fd >= 0);
138
139         if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) < 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);
149         assert(sd->type != DEVICE_TYPE_UNKNOWN);
150         assert(sd->node);
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
159         case DEVICE_TYPE_DRM:
160                 if (active) {
161                         /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
162                          * that so fail at all times and let caller retry in inactive 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 device automatically (ughh,
170                          * racy!). Hence, we just drop DRM-Master in case we were the first. */
171                         (void) sd_drmdropmaster(fd);
172                 break;
173
174         case DEVICE_TYPE_EVDEV:
175                 if (!active)
176                         sd_eviocrevoke(fd);
177                 break;
178
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
199         case DEVICE_TYPE_DRM:
200                 /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
201                  * keep the device 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
207         case DEVICE_TYPE_EVDEV:
208                 /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
209                 r = session_device_open(sd, true);
210                 if (r < 0)
211                         return r;
212
213                 /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
214                  * into a session and logind has been restarted right before. */
215                 safe_close(sd->fd);
216                 sd->fd = r;
217                 break;
218
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                 goto error;
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         if (sd->pushed_fd) {
413                 const char *m;
414
415                 /* Remove the pushed fd again, just in case. */
416
417                 m = strjoina("FDSTOREREMOVE=1\n"
418                              "FDNAME=session-", sd->session->id);
419
420                 (void) sd_notify(false, m);
421         }
422
423         session_device_stop(sd);
424         session_device_notify(sd, SESSION_DEVICE_RELEASE);
425         safe_close(sd->fd);
426
427         LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
428
429         hashmap_remove(sd->session->devices, &sd->dev);
430
431         free(sd->node);
432         free(sd);
433 }
434
435 void session_device_complete_pause(SessionDevice *sd) {
436         SessionDevice *iter;
437         Iterator i;
438
439         if (!sd->active)
440                 return;
441
442         session_device_stop(sd);
443
444         /* if not all devices are paused, wait for further completion events */
445         HASHMAP_FOREACH(iter, sd->session->devices, i)
446                 if (iter->active)
447                         return;
448
449         /* complete any pending session switch */
450         seat_complete_switch(sd->session->seat);
451 }
452
453 void session_device_resume_all(Session *s) {
454         SessionDevice *sd;
455         Iterator i;
456
457         assert(s);
458
459         HASHMAP_FOREACH(sd, s->devices, i) {
460                 if (sd->active)
461                         continue;
462
463                 if (session_device_start(sd) < 0)
464                         continue;
465                 if (session_device_save(sd) < 0)
466                         continue;
467                 session_device_notify(sd, SESSION_DEVICE_RESUME);
468         }
469 }
470
471 void session_device_pause_all(Session *s) {
472         SessionDevice *sd;
473         Iterator i;
474
475         assert(s);
476
477         HASHMAP_FOREACH(sd, s->devices, i) {
478                 if (!sd->active)
479                         continue;
480
481                 session_device_stop(sd);
482                 session_device_notify(sd, SESSION_DEVICE_PAUSE);
483         }
484 }
485
486 unsigned int session_device_try_pause_all(Session *s) {
487         unsigned num_pending = 0;
488         SessionDevice *sd;
489         Iterator i;
490
491         assert(s);
492
493         HASHMAP_FOREACH(sd, s->devices, i) {
494                 if (!sd->active)
495                         continue;
496
497                 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
498                 num_pending++;
499         }
500
501         return num_pending;
502 }
503
504 int session_device_save(SessionDevice *sd) {
505         const char *m;
506         int r;
507
508         assert(sd);
509
510         /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
511          * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
512          * don't have to handle them differently later.
513          *
514          * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
515          * is revoked. */
516
517         if (sd->pushed_fd)
518                 return 0;
519
520         m = strjoina("FDSTORE=1\n"
521                      "FDNAME=session", sd->session->id);
522
523         r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
524         if (r < 0)
525                 return r;
526
527         sd->pushed_fd = true;
528         return 1;
529 }
530
531 void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
532         assert(fd >= 0);
533         assert(sd);
534         assert(sd->fd < 0);
535         assert(!sd->active);
536
537         sd->fd = fd;
538         sd->active = active;
539 }