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

systemd / systemd / 15199265962

22 May 2025 09:40PM UTC coverage: 72.061% (-0.02%) from 72.079%
15199265962

push

github

bluca
tests: fix TEST-74-AUX-UTILS.varlinkctl.sh (#37562)

per Daan's explanation:
other subtests running as testuser apparently use systemd-run --user
--machine testuser@.host which turns user tracking in logind into "by
pin" mode. when the last pinning session exits it terminates the user.

299156 of 415145 relevant lines covered (72.06%)

703915.84 hits per line

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

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

3
#include <stdio.h>
4

5
#include "alloc-util.h"
6
#include "dbus-slice.h"
7
#include "dbus-unit.h"
8
#include "log.h"
9
#include "manager.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.h"
16
#include "unit-name.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,664✔
24
        Slice *s = ASSERT_PTR(SLICE(u));
2,664✔
25

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

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

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

36
        assert(s);
1,812✔
37

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

41
        old_state = s->state;
1,812✔
42
        s->state = state;
1,812✔
43

44
        if (state != old_state)
1,812✔
45
                log_unit_debug(UNIT(s), "Changed %s -> %s",
1,812✔
46
                               slice_state_to_string(old_state), slice_state_to_string(state));
47

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

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

56
        if (UNIT_GET_SLICE(u))
2,660✔
57
                return 0;
58

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

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

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

69
        assert(s);
2,660✔
70

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

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

82
        return 0;
83
}
84

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

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

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

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

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

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

106
        return 0;
107
}
108

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

112
        if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
2,660✔
113
                return 0;
114

115
        u->perpetual = true;
303✔
116

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

120
        u->default_dependencies = false;
303✔
121

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

127
        return 1;
128
}
129

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

133
        if (!MANAGER_IS_SYSTEM(u->manager))
2,660✔
134
                return 0;
135
        if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
1,732✔
136
                return 0;
137

138
        u->perpetual = true;
110✔
139

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

143
        u->default_dependencies = false;
110✔
144

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

150
        return 1;
151
}
152

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

157
        assert(u->load_state == UNIT_STUB);
2,660✔
158

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

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

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

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

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

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

186
        if (!u->description) {
2,660✔
187
                _cleanup_free_ char *tmp = NULL;
1,346✔
188

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

194
        return slice_verify(s);
2,660✔
195
}
196

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

200
        assert(s->state == SLICE_DEAD);
1,329✔
201

202
        if (s->deserialized_state != s->state)
1,329✔
203
                slice_set_state(s, s->deserialized_state);
961✔
204

205
        return 0;
1,329✔
206
}
207

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

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

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

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

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

226
        assert(s->state == SLICE_DEAD);
493✔
227

228
        r = unit_acquire_invocation_id(u);
493✔
229
        if (r < 0)
493✔
230
                return r;
231

232
        (void) unit_realize_cgroup(u);
493✔
233
        (void) unit_reset_accounting(u);
493✔
234

235
        slice_set_state(s, SLICE_ACTIVE);
493✔
236
        return 1;
493✔
237
}
238

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

242
        assert(s->state == SLICE_ACTIVE);
358✔
243

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

247
        slice_set_state(s, SLICE_DEAD);
358✔
248
        return 1;
358✔
249
}
250

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

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

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

259
        return 0;
979✔
260
}
261

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

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

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

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

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

281
        return 0;
782✔
282
}
283

284
static UnitActiveState slice_active_state(Unit *u) {
141,386✔
285
        Slice *s = ASSERT_PTR(SLICE(u));
141,386✔
286

287
        return state_translation_table[s->state];
141,386✔
288
}
289

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

293
        return slice_state_to_string(s->state);
1,893✔
294
}
295

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

300
        assert(m);
359✔
301
        assert(name);
359✔
302

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

310
        u->perpetual = true;
359✔
311
        SLICE(u)->deserialized_state = SLICE_ACTIVE;
359✔
312

313
        unit_add_to_load_queue(u);
359✔
314
        unit_add_to_dbus_queue(u);
359✔
315

316
        if (ret)
359✔
317
                *ret = u;
275✔
318

319
        return 0;
320
}
321

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

326
        assert(m);
275✔
327

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

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

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

340
        if (MANAGER_IS_SYSTEM(m))
275✔
341
                (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
84✔
342
}
275✔
343

344
static bool slice_can_freeze(const Unit *u) {
52✔
345
        assert(u);
52✔
346

347
        Unit *member;
52✔
348
        UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF)
207✔
349
                if (!unit_can_freeze(member))
97✔
350
                        return false;
21✔
351

352
        return true;
31✔
353
}
354

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

359
        assert(s);
×
360
        assert(action >= 0);
×
361
        assert(action < _FREEZER_ACTION_MAX);
×
362

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

372
        if (action == FREEZER_FREEZE)
×
373
                child_action = FREEZER_PARENT_FREEZE;
374
        else if (action == FREEZER_THAW)
×
375
                child_action = FREEZER_PARENT_THAW;
376
        else
377
                child_action = action;
×
378

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

387
        return unit_cgroup_freezer_action(s, action);
×
388
}
389

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

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

396
        unsigned n = 0;
189✔
397
        Unit *member;
189✔
398
        UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF) {
1,737✔
399
                if (member == ignore)
1,177✔
400
                        continue;
45✔
401

402
                if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(member)) ||
1,132✔
403
                    (with_pending && member->job && IN_SET(member->job->type, JOB_START, JOB_RESTART, JOB_RELOAD)))
6✔
404
                        n++;
559✔
405

406
                if (member->type == UNIT_SLICE)
1,132✔
407
                        n += slice_get_currently_active(SLICE(member), ignore, with_pending);
94✔
408
        }
409

410
        return n;
189✔
411
}
412

413
bool slice_concurrency_soft_max_reached(Slice *slice, Unit *ignore) {
9,529✔
414
        assert(slice);
9,529✔
415

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

420
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
9,524✔
421
        if (parent)
9,524✔
422
                return slice_concurrency_soft_max_reached(SLICE(parent), ignore);
4,585✔
423

424
        return false;
425
}
426

427
bool slice_concurrency_hard_max_reached(Slice *slice, Unit *ignore) {
69,932✔
428
        assert(slice);
69,932✔
429

430
        if (slice->concurrency_hard_max != UINT_MAX &&
69,932✔
431
            slice_get_currently_active(slice, ignore, /* with_pending= */ true) >= slice->concurrency_hard_max)
28✔
432
                return true;
433

434
        Unit *parent = UNIT_GET_SLICE(UNIT(slice));
69,930✔
435
        if (parent)
69,930✔
436
                return slice_concurrency_hard_max_reached(SLICE(parent), ignore);
26,418✔
437

438
        return false;
439
}
440

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

446
        .sections =
447
                "Unit\0"
448
                "Slice\0"
449
                "Install\0",
450
        .private_section = "Slice",
451

452
        .can_transient = true,
453
        .can_set_managed_oom = true,
454

455
        .init = slice_init,
456
        .load = slice_load,
457

458
        .coldplug = slice_coldplug,
459

460
        .dump = slice_dump,
461

462
        .start = slice_start,
463
        .stop = slice_stop,
464

465
        .freezer_action = slice_freezer_action,
466
        .can_freeze = slice_can_freeze,
467

468
        .serialize = slice_serialize,
469
        .deserialize_item = slice_deserialize_item,
470

471
        .active_state = slice_active_state,
472
        .sub_state_to_string = slice_sub_state_to_string,
473

474
        .bus_set_property = bus_slice_set_property,
475
        .bus_commit_properties = bus_slice_commit_properties,
476

477
        .enumerate_perpetual = slice_enumerate_perpetual,
478

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