• 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

88.5
/src/core/slice.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4

5
#include "alloc-util.h"
6
#include "dbus-slice.h"
7
#include "dbus-unit.h"
8
#include "fd-util.h"
9
#include "log.h"
10
#include "manager.h"
11
#include "serialize.h"
12
#include "slice.h"
13
#include "special.h"
14
#include "string-util.h"
15
#include "strv.h"
16
#include "unit-name.h"
17
#include "unit.h"
18

19
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
20
        [SLICE_DEAD]   = UNIT_INACTIVE,
21
        [SLICE_ACTIVE] = UNIT_ACTIVE,
22
};
23

24
static void slice_init(Unit *u) {
2,646✔
25
        Slice *s = ASSERT_PTR(SLICE(u));
2,646✔
26

27
        assert(u);
2,646✔
28
        assert(u->load_state == UNIT_STUB);
2,646✔
29

30
        u->ignore_on_isolate = true;
2,646✔
31
        s->concurrency_hard_max = UINT_MAX;
2,646✔
32
        s->concurrency_soft_max = UINT_MAX;
2,646✔
33
}
2,646✔
34

35
static void slice_set_state(Slice *s, SliceState state) {
1,799✔
36
        SliceState old_state;
1,799✔
37

38
        assert(s);
1,799✔
39

40
        if (s->state != state)
1,799✔
41
                bus_unit_send_pending_change_signal(UNIT(s), false);
1,799✔
42

43
        old_state = s->state;
1,799✔
44
        s->state = state;
1,799✔
45

46
        if (state != old_state)
1,799✔
47
                log_unit_debug(UNIT(s), "Changed %s -> %s",
1,799✔
48
                               slice_state_to_string(old_state), slice_state_to_string(state));
49

50
        unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
1,799✔
51
}
1,799✔
52

53
static int slice_add_parent_slice(Slice *s) {
2,642✔
54
        Unit *u = UNIT(ASSERT_PTR(s));
2,642✔
55
        _cleanup_free_ char *a = NULL;
2,642✔
56
        int r;
2,642✔
57

58
        if (UNIT_GET_SLICE(u))
2,642✔
59
                return 0;
60

61
        r = slice_build_parent_slice(u->id, &a);
2,642✔
62
        if (r <= 0) /* 0 means root slice */
2,642✔
63
                return r;
64

65
        return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT);
2,342✔
66
}
67

68
static int slice_add_default_dependencies(Slice *s) {
2,642✔
69
        int r;
2,642✔
70

71
        assert(s);
2,642✔
72

73
        if (!UNIT(s)->default_dependencies)
2,642✔
74
                return 0;
75

76
        /* Make sure slices are unloaded on shutdown */
77
        r = unit_add_two_dependencies_by_name(
4,458✔
78
                        UNIT(s),
2,229✔
79
                        UNIT_BEFORE, UNIT_CONFLICTS,
80
                        SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
81
        if (r < 0)
2,229✔
82
                return r;
×
83

84
        return 0;
85
}
86

87
static int slice_verify(Slice *s) {
2,642✔
88
        _cleanup_free_ char *parent = NULL;
2,642✔
89
        int r;
2,642✔
90

91
        assert(s);
2,642✔
92
        assert(UNIT(s)->load_state == UNIT_LOADED);
2,642✔
93

94
        if (!slice_name_is_valid(UNIT(s)->id))
2,642✔
95
                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
×
96

97
        r = slice_build_parent_slice(UNIT(s)->id, &parent);
2,642✔
98
        if (r < 0)
2,642✔
99
                return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
×
100

101
        /* If recursive errors are to be ignored, the parent slice should not be verified */
102
        if (UNIT(s)->manager && FLAGS_SET(UNIT(s)->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
2,642✔
103
                return 0;
104

105
        if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s)))
2,625✔
106
                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
×
107

108
        return 0;
109
}
110

111
static int slice_load_root_slice(Unit *u) {
2,642✔
112
        assert(u);
2,642✔
113

114
        if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
2,642✔
115
                return 0;
116

117
        u->perpetual = true;
300✔
118

119
        /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its
120
         * special semantics we synthesize it here, instead of relying on the unit file on disk. */
121

122
        u->default_dependencies = false;
300✔
123

124
        if (!u->description)
300✔
125
                u->description = strdup("Root Slice");
300✔
126
        if (!u->documentation)
300✔
127
                u->documentation = strv_new("man:systemd.special(7)");
300✔
128

129
        return 1;
130
}
131

132
static int slice_load_system_slice(Unit *u) {
2,642✔
133
        assert(u);
2,642✔
134

135
        if (!MANAGER_IS_SYSTEM(u->manager))
2,642✔
136
                return 0;
137
        if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
1,729✔
138
                return 0;
139

140
        u->perpetual = true;
110✔
141

142
        /* The system slice is a bit special. For example it is always running and cannot be terminated. Because of its
143
         * special semantics we synthesize it here, instead of relying on the unit file on disk. */
144

145
        u->default_dependencies = false;
110✔
146

147
        if (!u->description)
110✔
148
                u->description = strdup("System Slice");
110✔
149
        if (!u->documentation)
110✔
150
                u->documentation = strv_new("man:systemd.special(7)");
110✔
151

152
        return 1;
153
}
154

155
static int slice_load(Unit *u) {
2,642✔
156
        Slice *s = ASSERT_PTR(SLICE(u));
2,642✔
157
        int r;
2,642✔
158

159
        assert(u->load_state == UNIT_STUB);
2,642✔
160

161
        r = slice_load_root_slice(u);
2,642✔
162
        if (r < 0)
2,642✔
163
                return r;
164
        r = slice_load_system_slice(u);
2,642✔
165
        if (r < 0)
2,642✔
166
                return r;
167

168
        r = unit_load_fragment_and_dropin(u, false);
2,642✔
169
        if (r < 0)
2,642✔
170
                return r;
171

172
        if (u->load_state != UNIT_LOADED)
2,642✔
173
                return 0;
174

175
        /* This is a new unit? Then let's add in some extras */
176
        r = unit_patch_contexts(u);
2,642✔
177
        if (r < 0)
2,642✔
178
                return r;
179

180
        r = slice_add_parent_slice(s);
2,642✔
181
        if (r < 0)
2,642✔
182
                return r;
183

184
        r = slice_add_default_dependencies(s);
2,642✔
185
        if (r < 0)
2,642✔
186
                return r;
187

188
        if (!u->description) {
2,642✔
189
                _cleanup_free_ char *tmp = NULL;
1,342✔
190

191
                r = unit_name_to_path(u->id, &tmp);
1,342✔
192
                if (r >= 0)  /* Failure is ignored… */
1,342✔
193
                        u->description = strjoin("Slice ", tmp);
1,342✔
194
        }
195

196
        return slice_verify(s);
2,642✔
197
}
198

199
static int slice_coldplug(Unit *u) {
1,326✔
200
        Slice *s = ASSERT_PTR(SLICE(u));
1,326✔
201

202
        assert(s->state == SLICE_DEAD);
1,326✔
203

204
        if (s->deserialized_state != s->state)
1,326✔
205
                slice_set_state(s, s->deserialized_state);
958✔
206

207
        return 0;
1,326✔
208
}
209

210
static void slice_dump(Unit *u, FILE *f, const char *prefix) {
12✔
211
        Slice *s = ASSERT_PTR(SLICE(u));
12✔
212

213
        assert(s);
12✔
214
        assert(f);
12✔
215
        assert(prefix);
12✔
216

217
        fprintf(f,
12✔
218
                "%sSlice State: %s\n",
219
                prefix, slice_state_to_string(s->state));
220

221
        cgroup_context_dump(u, f, prefix);
12✔
222
}
12✔
223

224
static int slice_start(Unit *u) {
488✔
225
        Slice *s = ASSERT_PTR(SLICE(u));
488✔
226
        int r;
488✔
227

228
        assert(s->state == SLICE_DEAD);
488✔
229

230
        r = unit_acquire_invocation_id(u);
488✔
231
        if (r < 0)
488✔
232
                return r;
233

234
        (void) unit_realize_cgroup(u);
488✔
235
        (void) unit_reset_accounting(u);
488✔
236

237
        slice_set_state(s, SLICE_ACTIVE);
488✔
238
        return 1;
488✔
239
}
240

241
static int slice_stop(Unit *u) {
353✔
242
        Slice *s = ASSERT_PTR(SLICE(u));
353✔
243

244
        assert(s->state == SLICE_ACTIVE);
353✔
245

246
        /* We do not need to destroy the cgroup explicitly,
247
         * unit_notify() will do that for us anyway. */
248

249
        slice_set_state(s, SLICE_DEAD);
353✔
250
        return 1;
353✔
251
}
252

253
static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
979✔
254
        Slice *s = ASSERT_PTR(SLICE(u));
979✔
255

256
        assert(f);
979✔
257
        assert(fds);
979✔
258

259
        (void) serialize_item(f, "state", slice_state_to_string(s->state));
979✔
260

261
        return 0;
979✔
262
}
263

264
static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
782✔
265
        Slice *s = ASSERT_PTR(SLICE(u));
782✔
266

267
        assert(key);
782✔
268
        assert(value);
782✔
269
        assert(fds);
782✔
270

271
        if (streq(key, "state")) {
782✔
272
                SliceState state;
782✔
273

274
                state = slice_state_from_string(value);
782✔
275
                if (state < 0)
782✔
276
                        log_unit_debug(u, "Failed to parse state: %s", value);
×
277
                else
278
                        s->deserialized_state = state;
782✔
279

280
        } else
281
                log_unit_debug(u, "Unknown serialization key: %s", key);
×
282

283
        return 0;
782✔
284
}
285

286
static UnitActiveState slice_active_state(Unit *u) {
144,917✔
287
        Slice *s = ASSERT_PTR(SLICE(u));
144,917✔
288

289
        return state_translation_table[s->state];
144,917✔
290
}
291

292
static const char *slice_sub_state_to_string(Unit *u) {
1,863✔
293
        Slice *s = ASSERT_PTR(SLICE(u));
1,863✔
294

295
        return slice_state_to_string(s->state);
1,863✔
296
}
297

298
static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
356✔
299
        Unit *u;
356✔
300
        int r;
356✔
301

302
        assert(m);
356✔
303
        assert(name);
356✔
304

305
        u = manager_get_unit(m, name);
356✔
306
        if (!u) {
356✔
307
                r = unit_new_for_name(m, sizeof(Slice), name, &u);
356✔
308
                if (r < 0)
356✔
309
                        return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
×
310
        }
311

312
        u->perpetual = true;
356✔
313
        SLICE(u)->deserialized_state = SLICE_ACTIVE;
356✔
314

315
        unit_add_to_load_queue(u);
356✔
316
        unit_add_to_dbus_queue(u);
356✔
317

318
        if (ret)
356✔
319
                *ret = u;
272✔
320

321
        return 0;
322
}
323

324
static void slice_enumerate_perpetual(Manager *m) {
272✔
325
        Unit *u;
272✔
326
        int r;
272✔
327

328
        assert(m);
272✔
329

330
        r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
272✔
331
        if (r >= 0 && manager_owns_host_root_cgroup(m)) {
272✔
332
                Slice *s = SLICE(u);
30✔
333

334
                /* If we are managing the root cgroup then this means our root slice covers the whole system, which
335
                 * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's
336
                 * hence turn accounting on here, so that our APIs to query this data are available. */
337

338
                s->cgroup_context.cpu_accounting = true;
30✔
339
                s->cgroup_context.tasks_accounting = true;
30✔
340
                s->cgroup_context.memory_accounting = true;
30✔
341
        }
342

343
        if (MANAGER_IS_SYSTEM(m))
272✔
344
                (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
84✔
345
}
272✔
346

347
static bool slice_can_freeze(const Unit *u) {
54✔
348
        assert(u);
54✔
349

350
        Unit *member;
54✔
351
        UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF)
210✔
352
                if (!unit_can_freeze(member))
98✔
353
                        return false;
23✔
354

355
        return true;
31✔
356
}
357

358
static int slice_freezer_action(Unit *s, FreezerAction action) {
×
359
        FreezerAction child_action;
×
360
        int r;
×
361

362
        assert(s);
×
363
        assert(action >= 0);
×
364
        assert(action < _FREEZER_ACTION_MAX);
×
365

366
        if (action == FREEZER_FREEZE && !slice_can_freeze(s)) {
×
367
                /* We're intentionally only checking for FREEZER_FREEZE here and ignoring the
368
                 * _BY_PARENT variant. If we're being frozen by parent, that means someone has
369
                 * already checked if we can be frozen further up the call stack. No point to
370
                 * redo that work */
371
                log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice.");
×
372
                return 0;
×
373
        }
374

375
        if (action == FREEZER_FREEZE)
×
376
                child_action = FREEZER_PARENT_FREEZE;
377
        else if (action == FREEZER_THAW)
×
378
                child_action = FREEZER_PARENT_THAW;
379
        else
380
                child_action = action;
×
381

382
        Unit *member;
×
383
        UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF)
×
384
                if (UNIT_VTABLE(member)->freezer_action) {
×
385
                        r = UNIT_VTABLE(member)->freezer_action(member, child_action);
×
386
                        if (r < 0)
×
387
                                return r;
×
388
                }
389

390
        return unit_cgroup_freezer_action(s, action);
×
391
}
392

393
unsigned slice_get_currently_active(Slice *slice, Unit *ignore, bool with_pending) {
189✔
394
        Unit *u = ASSERT_PTR(UNIT(slice));
189✔
395

396
        /* If 'ignore' is non-NULL and a unit contained in this slice (or any below) we'll ignore it when
397
         * counting. */
398

399
        unsigned n = 0;
189✔
400
        Unit *member;
189✔
401
        UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF) {
1,716✔
402
                if (member == ignore)
1,156✔
403
                        continue;
45✔
404

405
                if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(member)) ||
1,111✔
406
                    (with_pending && member->job && IN_SET(member->job->type, JOB_START, JOB_RESTART, JOB_RELOAD)))
6✔
407
                        n++;
547✔
408

409
                if (member->type == UNIT_SLICE)
1,111✔
410
                        n += slice_get_currently_active(SLICE(member), ignore, with_pending);
94✔
411
        }
412

413
        return n;
189✔
414
}
415

416
bool slice_concurrency_soft_max_reached(Slice *slice, Unit *ignore) {
9,293✔
417
        assert(slice);
9,293✔
418

419
        if (slice->concurrency_soft_max != UINT_MAX &&
9,293✔
420
            slice_get_currently_active(slice, ignore, /* with_pending= */ false) >= slice->concurrency_soft_max)
17✔
421
                return true;
422

423
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
9,288✔
424
        if (parent)
9,288✔
425
                return slice_concurrency_soft_max_reached(SLICE(parent), ignore);
4,467✔
426

427
        return false;
428
}
429

430
bool slice_concurrency_hard_max_reached(Slice *slice, Unit *ignore) {
65,204✔
431
        assert(slice);
65,204✔
432

433
        if (slice->concurrency_hard_max != UINT_MAX &&
65,204✔
434
            slice_get_currently_active(slice, ignore, /* with_pending= */ true) >= slice->concurrency_hard_max)
28✔
435
                return true;
436

437
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
65,202✔
438
        if (parent)
65,202✔
439
                return slice_concurrency_hard_max_reached(SLICE(parent), ignore);
24,744✔
440

441
        return false;
442
}
443

444
const UnitVTable slice_vtable = {
445
        .object_size = sizeof(Slice),
446
        .cgroup_context_offset = offsetof(Slice, cgroup_context),
447
        .cgroup_runtime_offset = offsetof(Slice, cgroup_runtime),
448

449
        .sections =
450
                "Unit\0"
451
                "Slice\0"
452
                "Install\0",
453
        .private_section = "Slice",
454

455
        .can_transient = true,
456
        .can_set_managed_oom = true,
457

458
        .init = slice_init,
459
        .load = slice_load,
460

461
        .coldplug = slice_coldplug,
462

463
        .dump = slice_dump,
464

465
        .start = slice_start,
466
        .stop = slice_stop,
467

468
        .freezer_action = slice_freezer_action,
469
        .can_freeze = slice_can_freeze,
470

471
        .serialize = slice_serialize,
472
        .deserialize_item = slice_deserialize_item,
473

474
        .active_state = slice_active_state,
475
        .sub_state_to_string = slice_sub_state_to_string,
476

477
        .bus_set_property = bus_slice_set_property,
478
        .bus_commit_properties = bus_slice_commit_properties,
479

480
        .enumerate_perpetual = slice_enumerate_perpetual,
481

482
        .status_message_formats = {
483
                .finished_start_job = {
484
                        [JOB_DONE]       = "Created slice %s.",
485
                },
486
                .finished_stop_job = {
487
                        [JOB_DONE]       = "Removed slice %s.",
488
                },
489
        },
490
};
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