udev: String substitutions can be done in ENV, too
[systemd_ALT.git] / src / core / slice.c
blob82fd5e1f8da5da9066af61f284bbe368b773e1ba
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <errno.h>
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"
18 static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
19 [SLICE_DEAD] = UNIT_INACTIVE,
20 [SLICE_ACTIVE] = UNIT_ACTIVE
23 static void slice_init(Unit *u) {
24 assert(u);
25 assert(u->load_state == UNIT_STUB);
27 u->ignore_on_isolate = true;
30 static void slice_set_state(Slice *t, SliceState state) {
31 SliceState old_state;
32 assert(t);
34 if (t->state != state)
35 bus_unit_send_pending_change_signal(UNIT(t), false);
37 old_state = t->state;
38 t->state = state;
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));
46 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
49 static int slice_add_parent_slice(Slice *s) {
50 Unit *u = UNIT(s);
51 _cleanup_free_ char *a = NULL;
52 int r;
54 assert(s);
56 if (UNIT_GET_SLICE(u))
57 return 0;
59 r = slice_build_parent_slice(u->id, &a);
60 if (r <= 0) /* 0 means root slice */
61 return r;
63 return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT);
66 static int slice_add_default_dependencies(Slice *s) {
67 int r;
69 assert(s);
71 if (!UNIT(s)->default_dependencies)
72 return 0;
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;
82 return 0;
85 static int slice_verify(Slice *s) {
86 _cleanup_free_ char *parent = NULL;
87 int r;
89 assert(s);
90 assert(UNIT(s)->load_state == UNIT_LOADED);
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);
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");
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))
101 return 0;
103 if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s)))
104 return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
106 return 0;
109 static int slice_load_root_slice(Unit *u) {
110 assert(u);
112 if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
113 return 0;
115 u->perpetual = true;
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. */
120 u->default_dependencies = false;
122 if (!u->description)
123 u->description = strdup("Root Slice");
124 if (!u->documentation)
125 u->documentation = strv_new("man:systemd.special(7)");
127 return 1;
130 static int slice_load_system_slice(Unit *u) {
131 assert(u);
133 if (!MANAGER_IS_SYSTEM(u->manager))
134 return 0;
135 if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
136 return 0;
138 u->perpetual = true;
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. */
143 u->default_dependencies = false;
145 if (!u->description)
146 u->description = strdup("System Slice");
147 if (!u->documentation)
148 u->documentation = strv_new("man:systemd.special(7)");
150 return 1;
153 static int slice_load(Unit *u) {
154 Slice *s = SLICE(u);
155 int r;
157 assert(s);
158 assert(u->load_state == UNIT_STUB);
160 r = slice_load_root_slice(u);
161 if (r < 0)
162 return r;
163 r = slice_load_system_slice(u);
164 if (r < 0)
165 return r;
167 r = unit_load_fragment_and_dropin(u, false);
168 if (r < 0)
169 return r;
171 if (u->load_state != UNIT_LOADED)
172 return 0;
174 /* This is a new unit? Then let's add in some extras */
175 r = unit_patch_contexts(u);
176 if (r < 0)
177 return r;
179 r = slice_add_parent_slice(s);
180 if (r < 0)
181 return r;
183 r = slice_add_default_dependencies(s);
184 if (r < 0)
185 return r;
187 if (!u->description) {
188 _cleanup_free_ char *tmp = NULL;
190 r = unit_name_to_path(u->id, &tmp);
191 if (r >= 0) /* Failure is ignored… */
192 u->description = strjoin("Slice ", tmp);
195 return slice_verify(s);
198 static int slice_coldplug(Unit *u) {
199 Slice *t = SLICE(u);
201 assert(t);
202 assert(t->state == SLICE_DEAD);
204 if (t->deserialized_state != t->state)
205 slice_set_state(t, t->deserialized_state);
207 return 0;
210 static void slice_dump(Unit *u, FILE *f, const char *prefix) {
211 Slice *t = SLICE(u);
213 assert(t);
214 assert(f);
216 fprintf(f,
217 "%sSlice State: %s\n",
218 prefix, slice_state_to_string(t->state));
220 cgroup_context_dump(UNIT(t), f, prefix);
223 static int slice_start(Unit *u) {
224 Slice *t = SLICE(u);
225 int r;
227 assert(t);
228 assert(t->state == SLICE_DEAD);
230 r = unit_acquire_invocation_id(u);
231 if (r < 0)
232 return r;
234 (void) unit_realize_cgroup(u);
235 (void) unit_reset_accounting(u);
237 slice_set_state(t, SLICE_ACTIVE);
238 return 1;
241 static int slice_stop(Unit *u) {
242 Slice *t = SLICE(u);
244 assert(t);
245 assert(t->state == SLICE_ACTIVE);
247 /* We do not need to destroy the cgroup explicitly,
248 * unit_notify() will do that for us anyway. */
250 slice_set_state(t, SLICE_DEAD);
251 return 1;
254 static int slice_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
255 return unit_kill_common(u, who, signo, code, value, -1, -1, error);
258 static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
259 Slice *s = SLICE(u);
261 assert(s);
262 assert(f);
263 assert(fds);
265 (void) serialize_item(f, "state", slice_state_to_string(s->state));
267 return 0;
270 static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
271 Slice *s = SLICE(u);
273 assert(u);
274 assert(key);
275 assert(value);
276 assert(fds);
278 if (streq(key, "state")) {
279 SliceState state;
281 state = slice_state_from_string(value);
282 if (state < 0)
283 log_debug("Failed to parse state value %s", value);
284 else
285 s->deserialized_state = state;
287 } else
288 log_debug("Unknown serialization key '%s'", key);
290 return 0;
293 _pure_ static UnitActiveState slice_active_state(Unit *u) {
294 assert(u);
296 return state_translation_table[SLICE(u)->state];
299 _pure_ static const char *slice_sub_state_to_string(Unit *u) {
300 assert(u);
302 return slice_state_to_string(SLICE(u)->state);
305 static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
306 Unit *u;
307 int r;
309 assert(m);
310 assert(name);
312 u = manager_get_unit(m, name);
313 if (!u) {
314 r = unit_new_for_name(m, sizeof(Slice), name, &u);
315 if (r < 0)
316 return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
319 u->perpetual = true;
320 SLICE(u)->deserialized_state = SLICE_ACTIVE;
322 unit_add_to_load_queue(u);
323 unit_add_to_dbus_queue(u);
325 if (ret)
326 *ret = u;
328 return 0;
331 static void slice_enumerate_perpetual(Manager *m) {
332 Unit *u;
333 int r;
335 assert(m);
337 r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
338 if (r >= 0 && manager_owns_host_root_cgroup(m)) {
339 Slice *s = SLICE(u);
341 /* If we are managing the root cgroup then this means our root slice covers the whole system, which
342 * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's
343 * hence turn accounting on here, so that our APIs to query this data are available. */
345 s->cgroup_context.cpu_accounting = true;
346 s->cgroup_context.tasks_accounting = true;
347 s->cgroup_context.memory_accounting = true;
350 if (MANAGER_IS_SYSTEM(m))
351 (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
354 static bool slice_freezer_action_supported_by_children(Unit *s) {
355 Unit *member;
357 assert(s);
359 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
361 if (member->type == UNIT_SLICE &&
362 !slice_freezer_action_supported_by_children(member))
363 return false;
365 if (!UNIT_VTABLE(member)->freeze)
366 return false;
369 return true;
372 static int slice_freezer_action(Unit *s, FreezerAction action) {
373 Unit *member;
374 int r;
376 assert(s);
377 assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
379 if (action == FREEZER_FREEZE && !slice_freezer_action_supported_by_children(s)) {
380 log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
381 return 0;
384 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
385 if (!member->cgroup_realized)
386 continue;
388 if (action == FREEZER_FREEZE)
389 r = UNIT_VTABLE(member)->freeze(member);
390 else if (UNIT_VTABLE(member)->thaw)
391 r = UNIT_VTABLE(member)->thaw(member);
392 else
393 /* Thawing is requested but no corresponding method is available, ignore. */
394 r = 0;
395 if (r < 0)
396 return r;
399 return unit_cgroup_freezer_action(s, action);
402 static int slice_freeze(Unit *s) {
403 assert(s);
405 return slice_freezer_action(s, FREEZER_FREEZE);
408 static int slice_thaw(Unit *s) {
409 assert(s);
411 return slice_freezer_action(s, FREEZER_THAW);
414 static bool slice_can_freeze(Unit *s) {
415 assert(s);
417 return slice_freezer_action_supported_by_children(s);
420 const UnitVTable slice_vtable = {
421 .object_size = sizeof(Slice),
422 .cgroup_context_offset = offsetof(Slice, cgroup_context),
424 .sections =
425 "Unit\0"
426 "Slice\0"
427 "Install\0",
428 .private_section = "Slice",
430 .can_transient = true,
431 .can_set_managed_oom = true,
433 .init = slice_init,
434 .load = slice_load,
436 .coldplug = slice_coldplug,
438 .dump = slice_dump,
440 .start = slice_start,
441 .stop = slice_stop,
443 .kill = slice_kill,
445 .freeze = slice_freeze,
446 .thaw = slice_thaw,
447 .can_freeze = slice_can_freeze,
449 .serialize = slice_serialize,
450 .deserialize_item = slice_deserialize_item,
452 .active_state = slice_active_state,
453 .sub_state_to_string = slice_sub_state_to_string,
455 .bus_set_property = bus_slice_set_property,
456 .bus_commit_properties = bus_slice_commit_properties,
458 .enumerate_perpetual = slice_enumerate_perpetual,
460 .status_message_formats = {
461 .finished_start_job = {
462 [JOB_DONE] = "Created slice %s.",
464 .finished_stop_job = {
465 [JOB_DONE] = "Removed slice %s.",