Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/core/slice.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include <errno.h>
4
5
#include "alloc-util.h"
6
#include "dbus-slice.h"
7
#include "dbus-unit.h"
8
#include "log.h"
9
#include "serialize.h"
10
#include "slice.h"
11
#include "special.h"
12
#include "string-util.h"
13
#include "strv.h"
14
#include "unit-name.h"
15
#include "unit.h"
16
17
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
18
        [SLICE_DEAD] = UNIT_INACTIVE,
19
        [SLICE_ACTIVE] = UNIT_ACTIVE
20
};
21
22
6.24k
static void slice_init(Unit *u) {
23
6.24k
        assert(u);
24
6.24k
        assert(u->load_state == UNIT_STUB);
25
6.24k
26
6.24k
        u->ignore_on_isolate = true;
27
6.24k
}
28
29
0
static void slice_set_state(Slice *t, SliceState state) {
30
0
        SliceState old_state;
31
0
        assert(t);
32
0
33
0
        if (t->state != state)
34
0
                bus_unit_send_pending_change_signal(UNIT(t), false);
35
0
36
0
        old_state = t->state;
37
0
        t->state = state;
38
0
39
0
        if (state != old_state)
40
0
                log_debug("%s changed %s -> %s",
41
0
                          UNIT(t)->id,
42
0
                          slice_state_to_string(old_state),
43
0
                          slice_state_to_string(state));
44
0
45
0
        unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
46
0
}
47
48
5.71k
static int slice_add_parent_slice(Slice *s) {
49
5.71k
        Unit *u = UNIT(s), *parent;
50
5.71k
        _cleanup_free_ char *a = NULL;
51
5.71k
        int r;
52
5.71k
53
5.71k
        assert(s);
54
5.71k
55
5.71k
        if (UNIT_ISSET(u->slice))
56
5.71k
                return 0;
57
5.71k
58
5.71k
        r = slice_build_parent_slice(u->id, &a);
59
5.71k
        if (r <= 0) /* 0 means root slice */
60
1.18k
                return r;
61
4.53k
62
4.53k
        r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
63
4.53k
        if (r < 0)
64
0
                return r;
65
4.53k
66
4.53k
        unit_ref_set(&u->slice, u, parent);
67
4.53k
        return 0;
68
4.53k
}
69
70
5.29k
static int slice_add_default_dependencies(Slice *s) {
71
5.29k
        int r;
72
5.29k
73
5.29k
        assert(s);
74
5.29k
75
5.29k
        if (!UNIT(s)->default_dependencies)
76
770
                return 0;
77
4.52k
78
4.52k
        /* Make sure slices are unloaded on shutdown */
79
4.52k
        r = unit_add_two_dependencies_by_name(
80
4.52k
                        UNIT(s),
81
4.52k
                        UNIT_BEFORE, UNIT_CONFLICTS,
82
4.52k
                        SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
83
4.52k
        if (r < 0)
84
0
                return r;
85
4.52k
86
4.52k
        return 0;
87
4.52k
}
88
89
5.29k
static int slice_verify(Slice *s) {
90
5.29k
        _cleanup_free_ char *parent = NULL;
91
5.29k
        int r;
92
5.29k
93
5.29k
        assert(s);
94
5.29k
95
5.29k
        if (UNIT(s)->load_state != UNIT_LOADED)
96
0
                return 0;
97
5.29k
98
5.29k
        if (!slice_name_is_valid(UNIT(s)->id)) {
99
0
                log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
100
0
                return -ENOEXEC;
101
0
        }
102
5.29k
103
5.29k
        r = slice_build_parent_slice(UNIT(s)->id, &parent);
104
5.29k
        if (r < 0)
105
0
                return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
106
5.29k
107
5.29k
        if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) {
108
0
                log_unit_error(UNIT(s), "Located outside of parent slice. Refusing.");
109
0
                return -ENOEXEC;
110
0
        }
111
5.29k
112
5.29k
        return 0;
113
5.29k
}
114
115
5.71k
static int slice_load_root_slice(Unit *u) {
116
5.71k
        assert(u);
117
5.71k
118
5.71k
        if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
119
4.95k
                return 0;
120
760
121
760
        u->perpetual = true;
122
760
123
760
        /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its
124
760
         * special semantics we synthesize it here, instead of relying on the unit file on disk. */
125
760
126
760
        u->default_dependencies = false;
127
760
128
760
        if (!u->description)
129
760
                u->description = strdup("Root Slice");
130
760
        if (!u->documentation)
131
760
                u->documentation = strv_new("man:systemd.special(7)");
132
760
133
760
        return 1;
134
760
}
135
136
5.71k
static int slice_load_system_slice(Unit *u) {
137
5.71k
        assert(u);
138
5.71k
139
5.71k
        if (!MANAGER_IS_SYSTEM(u->manager))
140
5.71k
                return 0;
141
5.71k
        if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
142
5.70k
                return 0;
143
10
144
10
        u->perpetual = true;
145
10
146
10
        /* The system slice is a bit special. For example it is always running and cannot be terminated. Because of its
147
10
         * special semantics we synthesize it here, instead of relying on the unit file on disk. */
148
10
149
10
        u->default_dependencies = false;
150
10
151
10
        if (!u->description)
152
10
                u->description = strdup("System Slice");
153
10
        if (!u->documentation)
154
10
                u->documentation = strv_new("man:systemd.special(7)");
155
10
156
10
        return 1;
157
10
}
158
159
5.71k
static int slice_load(Unit *u) {
160
5.71k
        Slice *s = SLICE(u);
161
5.71k
        int r;
162
5.71k
163
5.71k
        assert(s);
164
5.71k
        assert(u->load_state == UNIT_STUB);
165
5.71k
166
5.71k
        r = slice_load_root_slice(u);
167
5.71k
        if (r < 0)
168
0
                return r;
169
5.71k
        r = slice_load_system_slice(u);
170
5.71k
        if (r < 0)
171
0
                return r;
172
5.71k
173
5.71k
        r = unit_load_fragment_and_dropin_optional(u);
174
5.71k
        if (r < 0)
175
0
                return r;
176
5.71k
177
5.71k
        /* This is a new unit? Then let's add in some extras */
178
5.71k
        if (u->load_state == UNIT_LOADED) {
179
5.71k
180
5.71k
                r = unit_patch_contexts(u);
181
5.71k
                if (r < 0)
182
0
                        return r;
183
5.71k
184
5.71k
                r = slice_add_parent_slice(s);
185
5.71k
                if (r < 0)
186
422
                        return r;
187
5.29k
188
5.29k
                r = slice_add_default_dependencies(s);
189
5.29k
                if (r < 0)
190
0
                        return r;
191
5.29k
        }
192
5.29k
193
5.29k
        return slice_verify(s);
194
5.29k
}
195
196
0
static int slice_coldplug(Unit *u) {
197
0
        Slice *t = SLICE(u);
198
0
199
0
        assert(t);
200
0
        assert(t->state == SLICE_DEAD);
201
0
202
0
        if (t->deserialized_state != t->state)
203
0
                slice_set_state(t, t->deserialized_state);
204
0
205
0
        return 0;
206
0
}
207
208
5.29k
static void slice_dump(Unit *u, FILE *f, const char *prefix) {
209
5.29k
        Slice *t = SLICE(u);
210
5.29k
211
5.29k
        assert(t);
212
5.29k
        assert(f);
213
5.29k
214
5.29k
        fprintf(f,
215
5.29k
                "%sSlice State: %s\n",
216
5.29k
                prefix, slice_state_to_string(t->state));
217
5.29k
218
5.29k
        cgroup_context_dump(&t->cgroup_context, f, prefix);
219
5.29k
}
220
221
0
static int slice_start(Unit *u) {
222
0
        Slice *t = SLICE(u);
223
0
        int r;
224
0
225
0
        assert(t);
226
0
        assert(t->state == SLICE_DEAD);
227
0
228
0
        r = unit_acquire_invocation_id(u);
229
0
        if (r < 0)
230
0
                return r;
231
0
232
0
        (void) unit_realize_cgroup(u);
233
0
        (void) unit_reset_accounting(u);
234
0
235
0
        slice_set_state(t, SLICE_ACTIVE);
236
0
        return 1;
237
0
}
238
239
0
static int slice_stop(Unit *u) {
240
0
        Slice *t = SLICE(u);
241
0
242
0
        assert(t);
243
0
        assert(t->state == SLICE_ACTIVE);
244
0
245
0
        /* We do not need to destroy the cgroup explicitly,
246
0
         * unit_notify() will do that for us anyway. */
247
0
248
0
        slice_set_state(t, SLICE_DEAD);
249
0
        return 1;
250
0
}
251
252
0
static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
253
0
        return unit_kill_common(u, who, signo, -1, -1, error);
254
0
}
255
256
0
static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
257
0
        Slice *s = SLICE(u);
258
0
259
0
        assert(s);
260
0
        assert(f);
261
0
        assert(fds);
262
0
263
0
        (void) serialize_item(f, "state", slice_state_to_string(s->state));
264
0
265
0
        return 0;
266
0
}
267
268
0
static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
269
0
        Slice *s = SLICE(u);
270
0
271
0
        assert(u);
272
0
        assert(key);
273
0
        assert(value);
274
0
        assert(fds);
275
0
276
0
        if (streq(key, "state")) {
277
0
                SliceState state;
278
0
279
0
                state = slice_state_from_string(value);
280
0
                if (state < 0)
281
0
                        log_debug("Failed to parse state value %s", value);
282
0
                else
283
0
                        s->deserialized_state = state;
284
0
285
0
        } else
286
0
                log_debug("Unknown serialization key '%s'", key);
287
0
288
0
        return 0;
289
0
}
290
291
19.4k
_pure_ static UnitActiveState slice_active_state(Unit *u) {
292
19.4k
        assert(u);
293
19.4k
294
19.4k
        return state_translation_table[SLICE(u)->state];
295
19.4k
}
296
297
0
_pure_ static const char *slice_sub_state_to_string(Unit *u) {
298
0
        assert(u);
299
0
300
0
        return slice_state_to_string(SLICE(u)->state);
301
0
}
302
303
0
static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
304
0
        Unit *u;
305
0
        int r;
306
0
307
0
        assert(m);
308
0
        assert(name);
309
0
310
0
        u = manager_get_unit(m, name);
311
0
        if (!u) {
312
0
                r = unit_new_for_name(m, sizeof(Slice), name, &u);
313
0
                if (r < 0)
314
0
                        return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
315
0
        }
316
0
317
0
        u->perpetual = true;
318
0
        SLICE(u)->deserialized_state = SLICE_ACTIVE;
319
0
320
0
        unit_add_to_load_queue(u);
321
0
        unit_add_to_dbus_queue(u);
322
0
323
0
        if (ret)
324
0
                *ret = u;
325
0
326
0
        return 0;
327
0
}
328
329
0
static void slice_enumerate_perpetual(Manager *m) {
330
0
        Unit *u;
331
0
        int r;
332
0
333
0
        assert(m);
334
0
335
0
        r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
336
0
        if (r >= 0 && manager_owns_host_root_cgroup(m)) {
337
0
                Slice *s = SLICE(u);
338
0
339
0
                /* If we are managing the root cgroup then this means our root slice covers the whole system, which
340
0
                 * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's
341
0
                 * hence turn accounting on here, so that our APIs to query this data are available. */
342
0
343
0
                s->cgroup_context.cpu_accounting = true;
344
0
                s->cgroup_context.tasks_accounting = true;
345
0
                s->cgroup_context.memory_accounting = true;
346
0
        }
347
0
348
0
        if (MANAGER_IS_SYSTEM(m))
349
0
                (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
350
0
}
351
352
const UnitVTable slice_vtable = {
353
        .object_size = sizeof(Slice),
354
        .cgroup_context_offset = offsetof(Slice, cgroup_context),
355
356
        .sections =
357
                "Unit\0"
358
                "Slice\0"
359
                "Install\0",
360
        .private_section = "Slice",
361
362
        .can_transient = true,
363
364
        .init = slice_init,
365
        .load = slice_load,
366
367
        .coldplug = slice_coldplug,
368
369
        .dump = slice_dump,
370
371
        .start = slice_start,
372
        .stop = slice_stop,
373
374
        .kill = slice_kill,
375
376
        .serialize = slice_serialize,
377
        .deserialize_item = slice_deserialize_item,
378
379
        .active_state = slice_active_state,
380
        .sub_state_to_string = slice_sub_state_to_string,
381
382
        .bus_vtable = bus_slice_vtable,
383
        .bus_set_property = bus_slice_set_property,
384
        .bus_commit_properties = bus_slice_commit_properties,
385
386
        .enumerate_perpetual = slice_enumerate_perpetual,
387
388
        .status_message_formats = {
389
                .finished_start_job = {
390
                        [JOB_DONE]       = "Created slice %s.",
391
                },
392
                .finished_stop_job = {
393
                        [JOB_DONE]       = "Removed slice %s.",
394
                },
395
        },
396
};