]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a016b922 LP |
2 | |
3 | #include <errno.h> | |
a016b922 | 4 | |
b5efdb8a | 5 | #include "alloc-util.h" |
07630cea | 6 | #include "dbus-slice.h" |
6fcbec6f | 7 | #include "dbus-unit.h" |
d9e45bc3 | 8 | #include "fd-util.h" |
a016b922 | 9 | #include "log.h" |
d68c645b | 10 | #include "serialize.h" |
cf0fbc49 | 11 | #include "slice.h" |
a016b922 | 12 | #include "special.h" |
07630cea LP |
13 | #include "string-util.h" |
14 | #include "strv.h" | |
a016b922 | 15 | #include "unit-name.h" |
efdb0237 | 16 | #include "unit.h" |
a016b922 LP |
17 | |
18 | static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { | |
19 | [SLICE_DEAD] = UNIT_INACTIVE, | |
20 | [SLICE_ACTIVE] = UNIT_ACTIVE | |
21 | }; | |
22 | ||
1b4cd0cf LP |
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 | ||
a016b922 LP |
30 | static void slice_set_state(Slice *t, SliceState state) { |
31 | SliceState old_state; | |
32 | assert(t); | |
33 | ||
6fcbec6f LP |
34 | if (t->state != state) |
35 | bus_unit_send_pending_change_signal(UNIT(t), false); | |
36 | ||
a016b922 LP |
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 | ||
96b09de5 | 46 | unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true); |
a016b922 LP |
47 | } |
48 | ||
4ad49000 | 49 | static int slice_add_parent_slice(Slice *s) { |
d219a2b0 | 50 | Unit *u = UNIT(s); |
a7894207 | 51 | _cleanup_free_ char *a = NULL; |
4ad49000 | 52 | int r; |
a016b922 LP |
53 | |
54 | assert(s); | |
55 | ||
12f64221 | 56 | if (UNIT_GET_SLICE(u)) |
a016b922 LP |
57 | return 0; |
58 | ||
a7894207 ZJS |
59 | r = slice_build_parent_slice(u->id, &a); |
60 | if (r <= 0) /* 0 means root slice */ | |
61 | return r; | |
a016b922 | 62 | |
d219a2b0 | 63 | return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT); |
a016b922 LP |
64 | } |
65 | ||
66 | static int slice_add_default_dependencies(Slice *s) { | |
3835b9aa LB |
67 | int r; |
68 | ||
a016b922 LP |
69 | assert(s); |
70 | ||
4c9ea260 LP |
71 | if (!UNIT(s)->default_dependencies) |
72 | return 0; | |
73 | ||
a016b922 | 74 | /* Make sure slices are unloaded on shutdown */ |
3835b9aa LB |
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; | |
a016b922 LP |
83 | } |
84 | ||
85 | static int slice_verify(Slice *s) { | |
93c47472 LP |
86 | _cleanup_free_ char *parent = NULL; |
87 | int r; | |
88 | ||
a016b922 | 89 | assert(s); |
75193d41 | 90 | assert(UNIT(s)->load_state == UNIT_LOADED); |
a016b922 | 91 | |
d85ff944 YW |
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); | |
93c47472 LP |
94 | |
95 | r = slice_build_parent_slice(UNIT(s)->id, &parent); | |
96 | if (r < 0) | |
f2341e0a | 97 | return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m"); |
93c47472 | 98 | |
12f64221 | 99 | if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s))) |
d85ff944 | 100 | return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing."); |
a016b922 LP |
101 | |
102 | return 0; | |
103 | } | |
104 | ||
8e4e851f LP |
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 | ||
f5869324 | 111 | u->perpetual = true; |
8e4e851f LP |
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; | |
8e4e851f LP |
117 | |
118 | if (!u->description) | |
119 | u->description = strdup("Root Slice"); | |
120 | if (!u->documentation) | |
bea1a013 | 121 | u->documentation = strv_new("man:systemd.special(7)"); |
8e4e851f LP |
122 | |
123 | return 1; | |
124 | } | |
125 | ||
d8e5a933 AJ |
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) | |
bea1a013 | 144 | u->documentation = strv_new("man:systemd.special(7)"); |
d8e5a933 AJ |
145 | |
146 | return 1; | |
147 | } | |
148 | ||
a016b922 LP |
149 | static int slice_load(Unit *u) { |
150 | Slice *s = SLICE(u); | |
151 | int r; | |
152 | ||
153 | assert(s); | |
4f4afc88 | 154 | assert(u->load_state == UNIT_STUB); |
a016b922 | 155 | |
8e4e851f LP |
156 | r = slice_load_root_slice(u); |
157 | if (r < 0) | |
158 | return r; | |
d8e5a933 AJ |
159 | r = slice_load_system_slice(u); |
160 | if (r < 0) | |
161 | return r; | |
162 | ||
c3620770 | 163 | r = unit_load_fragment_and_dropin(u, false); |
a016b922 LP |
164 | if (r < 0) |
165 | return r; | |
166 | ||
75193d41 ZJS |
167 | if (u->load_state != UNIT_LOADED) |
168 | return 0; | |
a016b922 | 169 | |
75193d41 ZJS |
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; | |
598459ce | 174 | |
75193d41 ZJS |
175 | r = slice_add_parent_slice(s); |
176 | if (r < 0) | |
177 | return r; | |
a016b922 | 178 | |
75193d41 ZJS |
179 | r = slice_add_default_dependencies(s); |
180 | if (r < 0) | |
181 | return r; | |
a016b922 | 182 | |
4dd21726 ZJS |
183 | if (!u->description) { |
184 | _cleanup_free_ char *tmp = NULL; | |
185 | ||
186 | r = unit_name_to_path(u->id, &tmp); | |
187 | if (r >= 0) /* Failure is ignored… */ | |
188 | u->description = strjoin("Slice ", tmp); | |
189 | } | |
190 | ||
a016b922 LP |
191 | return slice_verify(s); |
192 | } | |
193 | ||
be847e82 | 194 | static int slice_coldplug(Unit *u) { |
a016b922 LP |
195 | Slice *t = SLICE(u); |
196 | ||
197 | assert(t); | |
198 | assert(t->state == SLICE_DEAD); | |
199 | ||
200 | if (t->deserialized_state != t->state) | |
201 | slice_set_state(t, t->deserialized_state); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static void slice_dump(Unit *u, FILE *f, const char *prefix) { | |
207 | Slice *t = SLICE(u); | |
208 | ||
209 | assert(t); | |
210 | assert(f); | |
211 | ||
212 | fprintf(f, | |
213 | "%sSlice State: %s\n", | |
214 | prefix, slice_state_to_string(t->state)); | |
4ad49000 | 215 | |
bc0623df | 216 | cgroup_context_dump(UNIT(t), f, prefix); |
a016b922 LP |
217 | } |
218 | ||
219 | static int slice_start(Unit *u) { | |
220 | Slice *t = SLICE(u); | |
4b58153d | 221 | int r; |
a016b922 LP |
222 | |
223 | assert(t); | |
224 | assert(t->state == SLICE_DEAD); | |
225 | ||
4b58153d LP |
226 | r = unit_acquire_invocation_id(u); |
227 | if (r < 0) | |
228 | return r; | |
229 | ||
5ad096b3 | 230 | (void) unit_realize_cgroup(u); |
9b2559a1 | 231 | (void) unit_reset_accounting(u); |
a016b922 LP |
232 | |
233 | slice_set_state(t, SLICE_ACTIVE); | |
82a2b6bb | 234 | return 1; |
a016b922 LP |
235 | } |
236 | ||
237 | static int slice_stop(Unit *u) { | |
238 | Slice *t = SLICE(u); | |
239 | ||
240 | assert(t); | |
241 | assert(t->state == SLICE_ACTIVE); | |
242 | ||
4ad49000 LP |
243 | /* We do not need to destroy the cgroup explicitly, |
244 | * unit_notify() will do that for us anyway. */ | |
a016b922 LP |
245 | |
246 | slice_set_state(t, SLICE_DEAD); | |
82a2b6bb | 247 | return 1; |
a016b922 LP |
248 | } |
249 | ||
a721cd00 LP |
250 | static int slice_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) { |
251 | return unit_kill_common(u, who, signo, code, value, -1, -1, error); | |
a016b922 LP |
252 | } |
253 | ||
254 | static int slice_serialize(Unit *u, FILE *f, FDSet *fds) { | |
255 | Slice *s = SLICE(u); | |
256 | ||
257 | assert(s); | |
258 | assert(f); | |
259 | assert(fds); | |
260 | ||
d68c645b LP |
261 | (void) serialize_item(f, "state", slice_state_to_string(s->state)); |
262 | ||
a016b922 LP |
263 | return 0; |
264 | } | |
265 | ||
266 | static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { | |
267 | Slice *s = SLICE(u); | |
268 | ||
269 | assert(u); | |
270 | assert(key); | |
271 | assert(value); | |
272 | assert(fds); | |
273 | ||
274 | if (streq(key, "state")) { | |
275 | SliceState state; | |
276 | ||
277 | state = slice_state_from_string(value); | |
278 | if (state < 0) | |
279 | log_debug("Failed to parse state value %s", value); | |
280 | else | |
281 | s->deserialized_state = state; | |
282 | ||
283 | } else | |
284 | log_debug("Unknown serialization key '%s'", key); | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
289 | _pure_ static UnitActiveState slice_active_state(Unit *u) { | |
290 | assert(u); | |
291 | ||
292 | return state_translation_table[SLICE(u)->state]; | |
293 | } | |
294 | ||
295 | _pure_ static const char *slice_sub_state_to_string(Unit *u) { | |
296 | assert(u); | |
297 | ||
298 | return slice_state_to_string(SLICE(u)->state); | |
299 | } | |
300 | ||
cc6271f1 | 301 | static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) { |
efdb0237 LP |
302 | Unit *u; |
303 | int r; | |
304 | ||
305 | assert(m); | |
cc6271f1 | 306 | assert(name); |
efdb0237 | 307 | |
d8e5a933 | 308 | u = manager_get_unit(m, name); |
efdb0237 | 309 | if (!u) { |
d8e5a933 | 310 | r = unit_new_for_name(m, sizeof(Slice), name, &u); |
cc6271f1 LP |
311 | if (r < 0) |
312 | return log_error_errno(r, "Failed to allocate the special %s unit: %m", name); | |
efdb0237 LP |
313 | } |
314 | ||
f5869324 | 315 | u->perpetual = true; |
efdb0237 LP |
316 | SLICE(u)->deserialized_state = SLICE_ACTIVE; |
317 | ||
efdb0237 LP |
318 | unit_add_to_load_queue(u); |
319 | unit_add_to_dbus_queue(u); | |
cc6271f1 LP |
320 | |
321 | if (ret) | |
322 | *ret = u; | |
323 | ||
324 | return 0; | |
efdb0237 LP |
325 | } |
326 | ||
04eb582a | 327 | static void slice_enumerate_perpetual(Manager *m) { |
cc6271f1 LP |
328 | Unit *u; |
329 | int r; | |
330 | ||
d8e5a933 AJ |
331 | assert(m); |
332 | ||
cc6271f1 | 333 | r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u); |
611c4f8a | 334 | if (r >= 0 && manager_owns_host_root_cgroup(m)) { |
cc6271f1 LP |
335 | Slice *s = SLICE(u); |
336 | ||
337 | /* If we are managing the root cgroup then this means our root slice covers the whole system, which | |
338 | * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's | |
339 | * hence turn accounting on here, so that our APIs to query this data are available. */ | |
340 | ||
341 | s->cgroup_context.cpu_accounting = true; | |
342 | s->cgroup_context.tasks_accounting = true; | |
343 | s->cgroup_context.memory_accounting = true; | |
344 | } | |
d8e5a933 AJ |
345 | |
346 | if (MANAGER_IS_SYSTEM(m)) | |
cc6271f1 | 347 | (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL); |
d8e5a933 AJ |
348 | } |
349 | ||
d9e45bc3 MS |
350 | static bool slice_freezer_action_supported_by_children(Unit *s) { |
351 | Unit *member; | |
d9e45bc3 MS |
352 | |
353 | assert(s); | |
354 | ||
d219a2b0 | 355 | UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) { |
d9e45bc3 | 356 | |
5dc0c21b YW |
357 | if (member->type == UNIT_SLICE && |
358 | !slice_freezer_action_supported_by_children(member)) | |
359 | return false; | |
d9e45bc3 MS |
360 | |
361 | if (!UNIT_VTABLE(member)->freeze) | |
362 | return false; | |
363 | } | |
364 | ||
365 | return true; | |
366 | } | |
367 | ||
368 | static int slice_freezer_action(Unit *s, FreezerAction action) { | |
369 | Unit *member; | |
d9e45bc3 MS |
370 | int r; |
371 | ||
372 | assert(s); | |
373 | assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); | |
374 | ||
b458659a | 375 | if (action == FREEZER_FREEZE && !slice_freezer_action_supported_by_children(s)) { |
93c5b904 YW |
376 | log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice"); |
377 | return 0; | |
378 | } | |
d9e45bc3 | 379 | |
d219a2b0 | 380 | UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) { |
fcb0878f | 381 | if (!member->cgroup_realized) |
382 | continue; | |
383 | ||
d9e45bc3 MS |
384 | if (action == FREEZER_FREEZE) |
385 | r = UNIT_VTABLE(member)->freeze(member); | |
b458659a | 386 | else if (UNIT_VTABLE(member)->thaw) |
d9e45bc3 | 387 | r = UNIT_VTABLE(member)->thaw(member); |
b458659a | 388 | else |
389 | /* Thawing is requested but no corresponding method is available, ignore. */ | |
390 | r = 0; | |
d9e45bc3 MS |
391 | if (r < 0) |
392 | return r; | |
393 | } | |
394 | ||
d171e72e | 395 | return unit_cgroup_freezer_action(s, action); |
d9e45bc3 MS |
396 | } |
397 | ||
398 | static int slice_freeze(Unit *s) { | |
399 | assert(s); | |
400 | ||
401 | return slice_freezer_action(s, FREEZER_FREEZE); | |
402 | } | |
403 | ||
404 | static int slice_thaw(Unit *s) { | |
405 | assert(s); | |
406 | ||
407 | return slice_freezer_action(s, FREEZER_THAW); | |
408 | } | |
409 | ||
410 | static bool slice_can_freeze(Unit *s) { | |
411 | assert(s); | |
412 | ||
413 | return slice_freezer_action_supported_by_children(s); | |
414 | } | |
415 | ||
a016b922 LP |
416 | const UnitVTable slice_vtable = { |
417 | .object_size = sizeof(Slice), | |
718db961 LP |
418 | .cgroup_context_offset = offsetof(Slice, cgroup_context), |
419 | ||
a016b922 LP |
420 | .sections = |
421 | "Unit\0" | |
422 | "Slice\0" | |
423 | "Install\0", | |
4ad49000 | 424 | .private_section = "Slice", |
4ad49000 | 425 | |
17f62e9b | 426 | .can_transient = true, |
4d824a4e | 427 | .can_set_managed_oom = true, |
a016b922 | 428 | |
1b4cd0cf | 429 | .init = slice_init, |
a016b922 | 430 | .load = slice_load, |
4ad49000 | 431 | |
a016b922 LP |
432 | .coldplug = slice_coldplug, |
433 | ||
434 | .dump = slice_dump, | |
435 | ||
436 | .start = slice_start, | |
437 | .stop = slice_stop, | |
438 | ||
439 | .kill = slice_kill, | |
440 | ||
d9e45bc3 MS |
441 | .freeze = slice_freeze, |
442 | .thaw = slice_thaw, | |
443 | .can_freeze = slice_can_freeze, | |
444 | ||
a016b922 LP |
445 | .serialize = slice_serialize, |
446 | .deserialize_item = slice_deserialize_item, | |
447 | ||
448 | .active_state = slice_active_state, | |
449 | .sub_state_to_string = slice_sub_state_to_string, | |
450 | ||
8e2af478 LP |
451 | .bus_set_property = bus_slice_set_property, |
452 | .bus_commit_properties = bus_slice_commit_properties, | |
a016b922 | 453 | |
04eb582a | 454 | .enumerate_perpetual = slice_enumerate_perpetual, |
efdb0237 | 455 | |
a016b922 LP |
456 | .status_message_formats = { |
457 | .finished_start_job = { | |
4ad49000 | 458 | [JOB_DONE] = "Created slice %s.", |
a016b922 LP |
459 | }, |
460 | .finished_stop_job = { | |
4ad49000 | 461 | [JOB_DONE] = "Removed slice %s.", |
a016b922 LP |
462 | }, |
463 | }, | |
464 | }; |