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

systemd / systemd / 14606977008

22 Apr 2025 11:53PM UTC coverage: 72.18% (+0.1%) from 72.043%
14606977008

push

github

web-flow
network: enable ARP when IPv4LL and/or IPv4ACD is enabled (#37190)

51 of 56 new or added lines in 5 files covered. (91.07%)

1842 existing lines in 57 files now uncovered.

297069 of 411566 relevant lines covered (72.18%)

691007.91 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 "serialize.h"
11
#include "slice.h"
12
#include "special.h"
13
#include "string-util.h"
14
#include "strv.h"
15
#include "unit-name.h"
16
#include "unit.h"
17

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

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

26
        assert(u);
2,641✔
27
        assert(u->load_state == UNIT_STUB);
2,641✔
28

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

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

37
        assert(s);
1,796✔
38

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

42
        old_state = s->state;
1,796✔
43
        s->state = state;
1,796✔
44

45
        if (state != old_state)
1,796✔
46
                log_unit_debug(UNIT(s), "Changed %s -> %s",
3,494✔
47
                               slice_state_to_string(old_state), slice_state_to_string(state));
48

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

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

57
        if (UNIT_GET_SLICE(u))
2,637✔
58
                return 0;
59

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

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

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

70
        assert(s);
2,637✔
71

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

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

83
        return 0;
84
}
85

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

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

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

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

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

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

107
        return 0;
108
}
109

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

113
        if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
2,637✔
114
                return 0;
115

116
        u->perpetual = true;
299✔
117

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

121
        u->default_dependencies = false;
299✔
122

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

128
        return 1;
129
}
130

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

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

139
        u->perpetual = true;
110✔
140

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

144
        u->default_dependencies = false;
110✔
145

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

151
        return 1;
152
}
153

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

158
        assert(u->load_state == UNIT_STUB);
2,637✔
159

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

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

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

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

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

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

187
        if (!u->description) {
2,637✔
188
                _cleanup_free_ char *tmp = NULL;
1,341✔
189

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

195
        return slice_verify(s);
2,637✔
196
}
197

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

201
        assert(s->state == SLICE_DEAD);
1,325✔
202

203
        if (s->deserialized_state != s->state)
1,325✔
204
                slice_set_state(s, s->deserialized_state);
957✔
205

206
        return 0;
1,325✔
207
}
208

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

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

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

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

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

227
        assert(s->state == SLICE_DEAD);
487✔
228

229
        r = unit_acquire_invocation_id(u);
487✔
230
        if (r < 0)
487✔
231
                return r;
232

233
        (void) unit_realize_cgroup(u);
487✔
234
        (void) unit_reset_accounting(u);
487✔
235

236
        slice_set_state(s, SLICE_ACTIVE);
487✔
237
        return 1;
487✔
238
}
239

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

243
        assert(s->state == SLICE_ACTIVE);
352✔
244

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

248
        slice_set_state(s, SLICE_DEAD);
352✔
249
        return 1;
352✔
250
}
251

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

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

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

260
        return 0;
979✔
261
}
262

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

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

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

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

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

282
        return 0;
782✔
283
}
284

285
static UnitActiveState slice_active_state(Unit *u) {
138,483✔
286
        Slice *s = ASSERT_PTR(SLICE(u));
138,483✔
287

288
        return state_translation_table[s->state];
138,483✔
289
}
290

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

294
        return slice_state_to_string(s->state);
1,857✔
295
}
296

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

301
        assert(m);
355✔
302
        assert(name);
355✔
303

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

311
        u->perpetual = true;
355✔
312
        SLICE(u)->deserialized_state = SLICE_ACTIVE;
355✔
313

314
        unit_add_to_load_queue(u);
355✔
315
        unit_add_to_dbus_queue(u);
355✔
316

317
        if (ret)
355✔
318
                *ret = u;
271✔
319

320
        return 0;
321
}
322

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

327
        assert(m);
271✔
328

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

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

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

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

346
static bool slice_can_freeze(const Unit *u) {
52✔
347
        assert(u);
52✔
348

349
        Unit *member;
52✔
350
        UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF)
210✔
351
                if (!unit_can_freeze(member))
100✔
352
                        return false;
21✔
353

354
        return true;
31✔
355
}
356

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

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

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

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

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

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

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

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

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

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

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

412
        return n;
189✔
413
}
414

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

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

422
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
9,266✔
423
        if (parent)
9,266✔
424
                return slice_concurrency_soft_max_reached(SLICE(parent), ignore);
4,457✔
425

426
        return false;
427
}
428

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

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

436
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
65,138✔
437
        if (parent)
65,138✔
438
                return slice_concurrency_hard_max_reached(SLICE(parent), ignore);
24,721✔
439

440
        return false;
441
}
442

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

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

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

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

460
        .coldplug = slice_coldplug,
461

462
        .dump = slice_dump,
463

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

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

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

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

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

479
        .enumerate_perpetual = slice_enumerate_perpetual,
480

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