• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.98
/src/login/logind-session-device.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <linux/hidraw.h>
5
#include <linux/input.h>
6
#include <string.h>
7
#include <sys/ioctl.h>
8
#include <sys/types.h>
9

10
#include "sd-device.h"
11
#include "sd-daemon.h"
12

13
#include "alloc-util.h"
14
#include "bus-util.h"
15
#include "daemon-util.h"
16
#include "device-util.h"
17
#include "fd-util.h"
18
#include "logind.h"
19
#include "logind-device.h"
20
#include "logind-seat.h"
21
#include "logind-session.h"
22
#include "logind-session-dbus.h"
23
#include "logind-session-device.h"
24
#include "missing_drm.h"
25
#include "parse-util.h"
26

27
enum SessionDeviceNotifications {
28
        SESSION_DEVICE_RESUME,
29
        SESSION_DEVICE_TRY_PAUSE,
30
        SESSION_DEVICE_PAUSE,
31
        SESSION_DEVICE_RELEASE,
32
};
33

34
static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
×
35
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
×
36
        _cleanup_free_ char *path = NULL;
×
37
        const char *t = NULL;
×
38
        uint32_t major, minor;
×
39
        int r;
×
40

41
        assert(sd);
×
42

43
        major = major(sd->dev);
×
44
        minor = minor(sd->dev);
×
45

46
        if (!sd->session->controller)
×
47
                return 0;
48

49
        path = session_bus_path(sd->session);
×
50
        if (!path)
×
51
                return -ENOMEM;
52

53
        r = sd_bus_message_new_signal(
×
54
                        sd->session->manager->bus,
×
55
                        &m, path,
56
                        "org.freedesktop.login1.Session",
57
                        type == SESSION_DEVICE_RESUME ? "ResumeDevice" : "PauseDevice");
58
        if (!m)
×
59
                return r;
60

61
        r = sd_bus_message_set_destination(m, sd->session->controller);
×
62
        if (r < 0)
×
63
                return r;
64

65
        switch (type) {
×
66

67
        case SESSION_DEVICE_RESUME:
×
68
                r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
×
69
                if (r < 0)
×
70
                        return r;
71
                break;
72

73
        case SESSION_DEVICE_TRY_PAUSE:
74
                t = "pause";
75
                break;
76

77
        case SESSION_DEVICE_PAUSE:
78
                t = "force";
79
                break;
80

81
        case SESSION_DEVICE_RELEASE:
82
                t = "gone";
83
                break;
84

85
        default:
86
                return -EINVAL;
87
        }
88

89
        if (t) {
90
                r = sd_bus_message_append(m, "uus", major, minor, t);
×
91
                if (r < 0)
×
92
                        return r;
93
        }
94

95
        return sd_bus_send(sd->session->manager->bus, m, NULL);
×
96
}
97

98
static void sd_eviocrevoke(int fd) {
×
99
        static bool warned = false;
×
100

101
        assert(fd >= 0);
×
102

103
        if (!warned && ioctl(fd, EVIOCREVOKE, NULL) < 0) {
×
104
                if (errno == EINVAL) {
×
105
                        log_warning_errno(errno, "Kernel does not support evdev-revocation, continuing without revoking device access: %m");
×
106
                        warned = true;
×
107
                } else if (errno != ENODEV) {
×
108
                        log_warning_errno(errno, "Failed to revoke evdev device, continuing without revoking device access: %m");
×
109
                }
110
        }
111
}
×
112

113
static void sd_hidiocrevoke(int fd) {
×
114
        static bool warned = false;
×
115

116
        assert(fd >= 0);
×
117

118
        if (!warned && ioctl(fd, HIDIOCREVOKE, NULL) < 0) {
×
119
                if (errno == EINVAL) {
×
120
                        log_warning_errno(errno, "Kernel does not support hidraw-revocation, continuing without revoking device access: %m");
×
121
                        warned = true;
×
122
                } else if (errno != ENODEV)
×
123
                        log_warning_errno(errno, "Failed to revoke hidraw device, continuing without revoking device access: %m");
×
124
        }
125
}
×
126

127
static int sd_drmsetmaster(int fd) {
×
128
        assert(fd >= 0);
×
129
        return RET_NERRNO(ioctl(fd, DRM_IOCTL_SET_MASTER, 0));
×
130
}
131

132
static int sd_drmdropmaster(int fd) {
×
133
        assert(fd >= 0);
×
134
        return RET_NERRNO(ioctl(fd, DRM_IOCTL_DROP_MASTER, 0));
×
135
}
136

137
static int session_device_open(SessionDevice *sd, bool active) {
×
138
        _cleanup_close_ int fd = -EBADF;
×
139
        int r;
×
140

141
        assert(sd);
×
142
        assert(sd->type != DEVICE_TYPE_UNKNOWN);
×
143
        assert(sd->node);
×
144

145
        /* open device and try to get a udev_device from it */
146
        fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
×
147
        if (fd < 0)
×
148
                return -errno;
×
149

150
        switch (sd->type) {
×
151

152
        case DEVICE_TYPE_DRM:
×
153
                if (active) {
×
154
                        /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
155
                         * that so fail at all times and let caller retry in inactive state. */
156
                        r = sd_drmsetmaster(fd);
×
157
                        if (r < 0)
×
158
                                return r;
×
159
                } else
160
                        /* DRM-Master is granted to the first user who opens a device automatically (ughh,
161
                         * racy!). Hence, we just drop DRM-Master in case we were the first. */
162
                        (void) sd_drmdropmaster(fd);
×
163
                break;
164

165
        case DEVICE_TYPE_EVDEV:
×
166
                if (!active)
×
167
                        sd_eviocrevoke(fd);
×
168
                break;
169

170
        case DEVICE_TYPE_HIDRAW:
×
171
                if (!active)
×
172
                        sd_hidiocrevoke(fd);
×
173
                break;
174

175
        case DEVICE_TYPE_UNKNOWN:
176
        default:
177
                /* fallback for devices without synchronizations */
178
                ;
179
        }
180

181
        return TAKE_FD(fd);
182
}
183

184
static int session_device_start(SessionDevice *sd) {
×
185
        int r;
×
186

187
        assert(sd);
×
188
        assert(session_is_active(sd->session));
×
189

190
        if (sd->active)
×
191
                return 0;
×
192

193
        switch (sd->type) {
×
194

195
        case DEVICE_TYPE_DRM:
×
196
                if (sd->fd < 0)
×
197
                        return log_error_errno(SYNTHETIC_ERRNO(EBADF),
×
198
                                               "Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
199

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
        case DEVICE_TYPE_HIDRAW:
209
                /* Evdev/hidraw devices are revoked while inactive. Reopen it and we are fine. */
210
                r = session_device_open(sd, true);
×
211
                if (r < 0)
×
212
                        return r;
213

214
                /* For evdev/hidraw devices, the file descriptor might be left uninitialized. This might happen while resuming
215
                 * into a session and logind has been restarted right before. */
216
                close_and_replace(sd->fd, r);
×
217
                break;
×
218

219
        case DEVICE_TYPE_UNKNOWN:
×
220
        default:
221
                /* fallback for devices without synchronizations */
222
                ;
×
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

237
        case DEVICE_TYPE_DRM:
×
238
                if (sd->fd < 0) {
×
239
                        log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
×
240
                        return;
×
241
                }
242

243
                /* On DRM devices we simply drop DRM-Master but keep it open.
244
                 * This allows the user to keep resources allocated. The
245
                 * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
246
                 * circumventing this. */
247
                sd_drmdropmaster(sd->fd);
×
248
                break;
×
249

250
        case DEVICE_TYPE_EVDEV:
×
251
                /* Revoke access on evdev file-descriptors during deactivation.
252
                 * This will basically prevent any operations on the fd and
253
                 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
254
                 * protection this way. */
255
                sd_eviocrevoke(sd->fd);
×
256
                break;
×
257

258
        case DEVICE_TYPE_HIDRAW:
×
259
                /* Revoke access on hidraw file-descriptors during deactivation.
260
                 * This will basically prevent any operations on the fd and
261
                 * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
262
                 * protection this way. */
263
                sd_hidiocrevoke(sd->fd);
×
264
                break;
×
265

266
        case DEVICE_TYPE_UNKNOWN:
×
267
        default:
268
                /* fallback for devices without synchronization */
269
                ;
×
270
        }
271

272
        sd->active = false;
×
273
}
274

275
static DeviceType detect_device_type(sd_device *dev) {
×
276
        const char *sysname;
×
277

278
        if (sd_device_get_sysname(dev, &sysname) < 0)
×
279
                return DEVICE_TYPE_UNKNOWN;
×
280

281
        if (device_in_subsystem(dev, "drm")) {
×
282
                if (startswith(sysname, "card"))
×
283
                        return DEVICE_TYPE_DRM;
×
284

285
        } else if (device_in_subsystem(dev, "input")) {
×
286
                if (startswith(sysname, "event"))
×
287
                        return DEVICE_TYPE_EVDEV;
×
288
        } else if (device_in_subsystem(dev, "hidraw")) {
×
289
                if (startswith(sysname, "hidraw"))
×
290
                        return DEVICE_TYPE_HIDRAW;
×
291
        }
292

293
        return DEVICE_TYPE_UNKNOWN;
294
}
295

296
static int session_device_verify(SessionDevice *sd) {
×
297
        _cleanup_(sd_device_unrefp) sd_device *p = NULL;
×
298
        const char *sp, *node;
×
299
        sd_device *dev;
×
300
        int r;
×
301

302
        r = sd_device_new_from_devnum(&p, 'c', sd->dev);
×
303
        if (r < 0)
×
304
                return r;
305

306
        dev = p;
×
307

308
        if (sd_device_get_syspath(dev, &sp) < 0 ||
×
309
            sd_device_get_devname(dev, &node) < 0)
×
310
                return -EINVAL;
×
311

312
        /* detect device type so we can find the correct sysfs parent */
313
        sd->type = detect_device_type(dev);
×
314

315
        /* Prevent opening unsupported devices. Especially devices of
316
         * subsystem "input" must be opened via the evdev node as
317
         * we require EVIOCREVOKE. */
318
        switch (sd->type) {
×
319
        case DEVICE_TYPE_EVDEV:
×
320
                /* for evdev devices we need the parent node as device */
321
                if (sd_device_get_parent_with_subsystem_devtype(p, "input", NULL, &dev) < 0)
×
322
                        return -ENODEV;
323
                if (sd_device_get_syspath(dev, &sp) < 0)
×
324
                        return -ENODEV;
325
                break;
326

327
        case DEVICE_TYPE_DRM:
328
        case DEVICE_TYPE_HIDRAW:
329
                break;
330

331
        case  DEVICE_TYPE_UNKNOWN:
332
        default:
333
                return -ENODEV;
334
        }
335

336
        /* search for an existing seat device and return it if available */
337
        sd->device = hashmap_get(sd->session->manager->devices, sp);
×
338
        if (!sd->device) {
×
339
                /* The caller might have gotten the udev event before we were
340
                 * able to process it. Hence, fake the "add" event and let the
341
                 * logind-manager handle the new device. */
342
                r = manager_process_seat_device(sd->session->manager, dev);
×
343
                if (r < 0)
×
344
                        return r;
345

346
                /* if it's still not available, then the device is invalid */
347
                sd->device = hashmap_get(sd->session->manager->devices, sp);
×
348
                if (!sd->device)
×
349
                        return -ENODEV;
350
        }
351

352
        if (sd->device->seat != sd->session->seat)
×
353
                return -EPERM;
354

355
        return strdup_to(&sd->node, node);
×
356
}
357

358
int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret) {
×
359
        SessionDevice *sd;
×
360
        int r;
×
361

362
        assert(s);
×
363

364
        if (!s->seat)
×
365
                return -EPERM;
366

367
        sd = new(SessionDevice, 1);
×
368
        if (!sd)
×
369
                return -ENOMEM;
370

371
        *sd = (SessionDevice) {
×
372
                .session = s,
373
                .dev = dev,
374
                .fd = -EBADF,
375
                .type = DEVICE_TYPE_UNKNOWN,
376
        };
377

378
        r = session_device_verify(sd);
×
379
        if (r < 0)
×
380
                goto error;
×
381

382
        r = hashmap_put(s->devices, &sd->dev, sd);
×
383
        if (r < 0)
×
384
                goto error;
×
385

386
        if (open_device) {
×
387
                /* Open the device for the first time. We need a valid fd to pass back
388
                 * to the caller. If the session is not active, this _might_ immediately
389
                 * revoke access and thus invalidate the fd. But this is still needed
390
                 * to pass a valid fd back. */
391
                sd->active = session_is_active(s);
×
392
                r = session_device_open(sd, sd->active);
×
393
                if (r < 0) {
×
394
                        /* EINVAL _may_ mean a master is active; retry inactive */
395
                        if (sd->active && r == -EINVAL) {
×
396
                                sd->active = false;
×
397
                                r = session_device_open(sd, false);
×
398
                        }
399
                        if (r < 0)
×
400
                                goto error;
×
401
                }
402
                sd->fd = r;
×
403
        }
404

405
        LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
×
406

407
        if (ret)
×
408
                *ret = sd;
×
409

410
        return 0;
411

412
error:
×
413
        hashmap_remove(s->devices, &sd->dev);
×
414
        free(sd->node);
×
415
        free(sd);
×
416
        return r;
×
417
}
418

419
SessionDevice *session_device_free(SessionDevice *sd) {
×
420
        if (!sd)
×
421
                return NULL;
422

423
        /* Make sure to remove the pushed fd. */
424
        if (sd->pushed_fd)
×
425
                (void) notify_remove_fd_warnf("session-%s-device-%u-%u", sd->session->id, major(sd->dev), minor(sd->dev));
×
426

427
        session_device_stop(sd);
×
428
        session_device_notify(sd, SESSION_DEVICE_RELEASE);
×
429
        safe_close(sd->fd);
×
430

431
        LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
×
432

433
        hashmap_remove(sd->session->devices, &sd->dev);
×
434

435
        free(sd->node);
×
436

437
        return mfree(sd);
×
438
}
439

440
void session_device_complete_pause(SessionDevice *sd) {
×
441
        SessionDevice *iter;
×
442

443
        if (!sd->active)
×
444
                return;
×
445

446
        session_device_stop(sd);
×
447

448
        /* if not all devices are paused, wait for further completion events */
449
        HASHMAP_FOREACH(iter, sd->session->devices)
×
450
                if (iter->active)
×
451
                        return;
×
452

453
        /* complete any pending session switch */
454
        seat_complete_switch(sd->session->seat);
×
455
}
456

457
void session_device_resume_all(Session *s) {
×
458
        SessionDevice *sd;
×
459

460
        assert(s);
×
461

462
        HASHMAP_FOREACH(sd, s->devices) {
×
463
                if (sd->active)
×
464
                        continue;
×
465

466
                if (session_device_start(sd) < 0)
×
467
                        continue;
×
468
                if (session_device_save(sd) < 0)
×
469
                        continue;
×
470

471
                session_device_notify(sd, SESSION_DEVICE_RESUME);
×
472
        }
473
}
×
474

475
void session_device_pause_all(Session *s) {
32✔
476
        SessionDevice *sd;
32✔
477

478
        assert(s);
32✔
479

480
        HASHMAP_FOREACH(sd, s->devices) {
32✔
481
                if (!sd->active)
×
482
                        continue;
×
483

484
                session_device_stop(sd);
×
485
                session_device_notify(sd, SESSION_DEVICE_PAUSE);
×
486
        }
487
}
32✔
488

489
unsigned session_device_try_pause_all(Session *s) {
×
490
        unsigned num_pending = 0;
×
491
        SessionDevice *sd;
×
492

493
        assert(s);
×
494

495
        HASHMAP_FOREACH(sd, s->devices) {
×
496
                if (!sd->active)
×
497
                        continue;
×
498

499
                session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
×
500
                num_pending++;
×
501
        }
502

503
        return num_pending;
×
504
}
505

506
int session_device_save(SessionDevice *sd) {
×
507
        const char *id;
×
508
        int r;
×
509

510
        assert(sd);
×
511

512
        /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
513
         * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
514
         * don't have to handle them differently later.
515
         *
516
         * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
517
         * is revoked. */
518

519
        if (sd->pushed_fd)
×
520
                return 0;
521

522
        /* Session ID does not contain separators. */
523
        id = sd->session->id;
×
524
        assert(*(id + strcspn(id, "-\n")) == '\0');
×
525

526
        r = notify_push_fdf(sd->fd, "session-%s-device-%u-%u", id, major(sd->dev), minor(sd->dev));
×
527
        if (r < 0)
×
528
                return r;
529

530
        sd->pushed_fd = true;
×
531
        return 1;
×
532
}
533

534
void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
×
535
        assert(fd >= 0);
×
536
        assert(sd);
×
537
        assert(sd->fd < 0);
×
538
        assert(!sd->active);
×
539

540
        sd->fd = fd;
×
541
        sd->pushed_fd = true;
×
542
        sd->active = active;
×
543
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc