]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/slice.c
595f704a17a43a3ebe8ed8616ccf3c5d12092980
[thirdparty/systemd.git] / 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) {
24 assert(u);
25 assert(u->load_state == UNIT_STUB);
26
27 u->ignore_on_isolate = true;
28 }
29
30 static void slice_set_state(Slice *t, SliceState state) {
31 SliceState old_state;
32 assert(t);
33
34 if (t->state != state)
35 bus_unit_send_pending_change_signal(UNIT(t), false);
36
37 old_state = t->state;
38 t->state = state;
39
40 if (state != old_state)
41 log_debug("%s changed %s -> %s",
42 UNIT(t)->id,
43 slice_state_to_string(old_state),
44 slice_state_to_string(state));
45
46 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
47 }
48
49 static int slice_add_parent_slice(Slice *s) {
50 Unit *u = UNIT(s);
51 _cleanup_free_ char *a = NULL;
52 int r;
53
54 assert(s);
55
56 if (UNIT_GET_SLICE(u))
57 return 0;
58
59 r = slice_build_parent_slice(u->id, &a);
60 if (r <= 0) /* 0 means root slice */
61 return r;
62
63 return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT);
64 }
65
66 static int slice_add_default_dependencies(Slice *s) {
67 int r;
68
69 assert(s);
70
71 if (!UNIT(s)->default_dependencies)
72 return 0;
73
74 /* Make sure slices are unloaded on shutdown */
75 r = unit_add_two_dependencies_by_name(
76 UNIT(s),
77 UNIT_BEFORE, UNIT_CONFLICTS,
78 SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
79 if (r < 0)
80 return r;
81
82 return 0;
83 }
84
85 static int slice_verify(Slice *s) {
86 _cleanup_free_ char *parent = NULL;
87 int r;
88
89 assert(s);
90 assert(UNIT(s)->load_state == UNIT_LOADED);
91
92 if (!slice_name_is_valid(UNIT(s)->id))
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);
96 if (r < 0)
97 return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
98
99 if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s)))
100 return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
101
102 return 0;
103 }
104
105 static int slice_load_root_slice(Unit *u) {
106 assert(u);
107
108 if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
109 return 0;
110
111 u->perpetual = true;
112
113 /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its
114 * special semantics we synthesize it here, instead of relying on the unit file on disk. */
115
116 u->default_dependencies = false;
117
118 if (!u->description)
119 u->description = strdup("Root Slice");
120 if (!u->documentation)
121 u->documentation = strv_new("man:systemd.special(7)");
122
123 return 1;
124 }
125
126 static int slice_load_system_slice(Unit *u) {
127 assert(u);
128
129 if (!MANAGER_IS_SYSTEM(u->manager))
130 return 0;
131 if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
132 return 0;
133
134 u->perpetual = true;
135
136 /* The system slice is a bit special. For example it is always running and cannot be terminated. Because of its
137 * special semantics we synthesize it here, instead of relying on the unit file on disk. */
138
139 u->default_dependencies = false;
140
141 if (!u->description)
142 u->description = strdup("System Slice");
143 if (!u->documentation)
144 u->documentation = strv_new("man:systemd.special(7)");
145
146 return 1;
147 }
148
149 static int slice_load(Unit *u) {
150 Slice *s = SLICE(u);
151 int r;
152
153 assert(s);
154 assert(u->load_state == UNIT_STUB);
155
156 r = slice_load_root_slice(u);
157 if (r < 0)
158 return r;
159 r = slice_load_system_slice(u);
160 if (r < 0)
161 return r;
162
163 r = unit_load_fragment_and_dropin(u, false);
164 if (r < 0)
165 return r;
166
167 if (u->load_state != UNIT_LOADED)
168 return 0;
169
170 /* This is a new unit? Then let's add in some extras */
171 r = unit_patch_contexts(u);
172 if (r < 0)
173 return r;
174
175 r = slice_add_parent_slice(s);
176 if (r < 0)
177 return r;
178
179 r = slice_add_default_dependencies(s);
180 if (r < 0)
181 return r;
182
183 return slice_verify(s);
184 }
185
186 static int slice_coldplug(Unit *u) {
187 Slice *t = SLICE(u);
188
189 assert(t);
190 assert(t->state == SLICE_DEAD);
191
192 if (t->deserialized_state != t->state)
193 slice_set_state(t, t->deserialized_state);
194
195 return 0;
196 }
197
198 static void slice_dump(Unit *u, FILE *f, const char *prefix) {
199 Slice *t = SLICE(u);
200
201 assert(t);
202 assert(f);
203
204 fprintf(f,
205 "%sSlice State: %s\n",
206 prefix, slice_state_to_string(t->state));
207
208 cgroup_context_dump(UNIT(t), f, prefix);
209 }
210
211 static int slice_start(Unit *u) {
212 Slice *t = SLICE(u);
213 int r;
214
215 assert(t);
216 assert(t->state == SLICE_DEAD);
217
218 r = unit_acquire_invocation_id(u);
219 if (r < 0)
220 return r;
221
222 (void) unit_realize_cgroup(u);
223 (void) unit_reset_accounting(u);
224
225 slice_set_state(t, SLICE_ACTIVE);
226 return 1;
227 }
228
229 static int slice_stop(Unit *u) {
230 Slice *t = SLICE(u);
231
232 assert(t);
233 assert(t->state == SLICE_ACTIVE);
234
235 /* We do not need to destroy the cgroup explicitly,
236 * unit_notify() will do that for us anyway. */
237
238 slice_set_state(t, SLICE_DEAD);
239 return 1;
240 }
241
242 static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
243 return unit_kill_common(u, who, signo, -1, -1, error);
244 }
245
246 static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
247 Slice *s = SLICE(u);
248
249 assert(s);
250 assert(f);
251 assert(fds);
252
253 (void) serialize_item(f, "state", slice_state_to_string(s->state));
254
255 return 0;
256 }
257
258 static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
259 Slice *s = SLICE(u);
260
261 assert(u);
262 assert(key);
263 assert(value);
264 assert(fds);
265
266 if (streq(key, "state")) {
267 SliceState state;
268
269 state = slice_state_from_string(value);
270 if (state < 0)
271 log_debug("Failed to parse state value %s", value);
272 else
273 s->deserialized_state = state;
274
275 } else
276 log_debug("Unknown serialization key '%s'", key);
277
278 return 0;
279 }
280
281 _pure_ static UnitActiveState slice_active_state(Unit *u) {
282 assert(u);
283
284 return state_translation_table[SLICE(u)->state];
285 }
286
287 _pure_ static const char *slice_sub_state_to_string(Unit *u) {
288 assert(u);
289
290 return slice_state_to_string(SLICE(u)->state);
291 }
292
293 static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
294 Unit *u;
295 int r;
296
297 assert(m);
298 assert(name);
299
300 u = manager_get_unit(m, name);
301 if (!u) {
302 r = unit_new_for_name(m, sizeof(Slice), name, &u);
303 if (r < 0)
304 return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
305 }
306
307 u->perpetual = true;
308 SLICE(u)->deserialized_state = SLICE_ACTIVE;
309
310 unit_add_to_load_queue(u);
311 unit_add_to_dbus_queue(u);
312
313 if (ret)
314 *ret = u;
315
316 return 0;
317 }
318
319 static void slice_enumerate_perpetual(Manager *m) {
320 Unit *u;
321 int r;
322
323 assert(m);
324
325 r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
326 if (r >= 0 && manager_owns_host_root_cgroup(m)) {
327 Slice *s = SLICE(u);
328
329 /* If we are managing the root cgroup then this means our root slice covers the whole system, which
330 * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's
331 * hence turn accounting on here, so that our APIs to query this data are available. */
332
333 s->cgroup_context.cpu_accounting = true;
334 s->cgroup_context.tasks_accounting = true;
335 s->cgroup_context.memory_accounting = true;
336 }
337
338 if (MANAGER_IS_SYSTEM(m))
339 (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
340 }
341
342 static bool slice_freezer_action_supported_by_children(Unit *s) {
343 Unit *member;
344 int r;
345
346 assert(s);
347
348 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
349
350 if (member->type == UNIT_SLICE) {
351 r = slice_freezer_action_supported_by_children(member);
352 if (!r)
353 return r;
354 }
355
356 if (!UNIT_VTABLE(member)->freeze)
357 return false;
358 }
359
360 return true;
361 }
362
363 static int slice_freezer_action(Unit *s, FreezerAction action) {
364 Unit *member;
365 int r;
366
367 assert(s);
368 assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
369
370 if (!slice_freezer_action_supported_by_children(s)) {
371 log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
372 return 0;
373 }
374
375 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
376 if (action == FREEZER_FREEZE)
377 r = UNIT_VTABLE(member)->freeze(member);
378 else
379 r = UNIT_VTABLE(member)->thaw(member);
380 if (r < 0)
381 return r;
382 }
383
384 r = unit_cgroup_freezer_action(s, action);
385 if (r < 0)
386 return r;
387
388 return 1;
389 }
390
391 static int slice_freeze(Unit *s) {
392 assert(s);
393
394 return slice_freezer_action(s, FREEZER_FREEZE);
395 }
396
397 static int slice_thaw(Unit *s) {
398 assert(s);
399
400 return slice_freezer_action(s, FREEZER_THAW);
401 }
402
403 static bool slice_can_freeze(Unit *s) {
404 assert(s);
405
406 return slice_freezer_action_supported_by_children(s);
407 }
408
409 const UnitVTable slice_vtable = {
410 .object_size = sizeof(Slice),
411 .cgroup_context_offset = offsetof(Slice, cgroup_context),
412
413 .sections =
414 "Unit\0"
415 "Slice\0"
416 "Install\0",
417 .private_section = "Slice",
418
419 .can_transient = true,
420 .can_set_managed_oom = true,
421
422 .init = slice_init,
423 .load = slice_load,
424
425 .coldplug = slice_coldplug,
426
427 .dump = slice_dump,
428
429 .start = slice_start,
430 .stop = slice_stop,
431
432 .kill = slice_kill,
433
434 .freeze = slice_freeze,
435 .thaw = slice_thaw,
436 .can_freeze = slice_can_freeze,
437
438 .serialize = slice_serialize,
439 .deserialize_item = slice_deserialize_item,
440
441 .active_state = slice_active_state,
442 .sub_state_to_string = slice_sub_state_to_string,
443
444 .bus_set_property = bus_slice_set_property,
445 .bus_commit_properties = bus_slice_commit_properties,
446
447 .enumerate_perpetual = slice_enumerate_perpetual,
448
449 .status_message_formats = {
450 .finished_start_job = {
451 [JOB_DONE] = "Created slice %s.",
452 },
453 .finished_stop_job = {
454 [JOB_DONE] = "Removed slice %s.",
455 },
456 },
457 };