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
slice_init(Unit * u)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
slice_set_state(Slice * t,SliceState state)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
slice_add_parent_slice(Slice * s)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
slice_add_default_dependencies(Slice * s)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
slice_verify(Slice * s)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
slice_load_root_slice(Unit * u)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
slice_load_system_slice(Unit * u)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
slice_load(Unit * u)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 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
191 return slice_verify(s);
192 }
193
slice_coldplug(Unit * u)194 static int slice_coldplug(Unit *u) {
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
slice_dump(Unit * u,FILE * f,const char * prefix)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));
215
216 cgroup_context_dump(UNIT(t), f, prefix);
217 }
218
slice_start(Unit * u)219 static int slice_start(Unit *u) {
220 Slice *t = SLICE(u);
221 int r;
222
223 assert(t);
224 assert(t->state == SLICE_DEAD);
225
226 r = unit_acquire_invocation_id(u);
227 if (r < 0)
228 return r;
229
230 (void) unit_realize_cgroup(u);
231 (void) unit_reset_accounting(u);
232
233 slice_set_state(t, SLICE_ACTIVE);
234 return 1;
235 }
236
slice_stop(Unit * u)237 static int slice_stop(Unit *u) {
238 Slice *t = SLICE(u);
239
240 assert(t);
241 assert(t->state == SLICE_ACTIVE);
242
243 /* We do not need to destroy the cgroup explicitly,
244 * unit_notify() will do that for us anyway. */
245
246 slice_set_state(t, SLICE_DEAD);
247 return 1;
248 }
249
slice_kill(Unit * u,KillWho who,int signo,sd_bus_error * error)250 static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
251 return unit_kill_common(u, who, signo, -1, -1, error);
252 }
253
slice_serialize(Unit * u,FILE * f,FDSet * fds)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
261 (void) serialize_item(f, "state", slice_state_to_string(s->state));
262
263 return 0;
264 }
265
slice_deserialize_item(Unit * u,const char * key,const char * value,FDSet * fds)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
slice_active_state(Unit * u)289 _pure_ static UnitActiveState slice_active_state(Unit *u) {
290 assert(u);
291
292 return state_translation_table[SLICE(u)->state];
293 }
294
slice_sub_state_to_string(Unit * u)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
slice_make_perpetual(Manager * m,const char * name,Unit ** ret)301 static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
302 Unit *u;
303 int r;
304
305 assert(m);
306 assert(name);
307
308 u = manager_get_unit(m, name);
309 if (!u) {
310 r = unit_new_for_name(m, sizeof(Slice), name, &u);
311 if (r < 0)
312 return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
313 }
314
315 u->perpetual = true;
316 SLICE(u)->deserialized_state = SLICE_ACTIVE;
317
318 unit_add_to_load_queue(u);
319 unit_add_to_dbus_queue(u);
320
321 if (ret)
322 *ret = u;
323
324 return 0;
325 }
326
slice_enumerate_perpetual(Manager * m)327 static void slice_enumerate_perpetual(Manager *m) {
328 Unit *u;
329 int r;
330
331 assert(m);
332
333 r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
334 if (r >= 0 && manager_owns_host_root_cgroup(m)) {
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 }
345
346 if (MANAGER_IS_SYSTEM(m))
347 (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
348 }
349
slice_freezer_action_supported_by_children(Unit * s)350 static bool slice_freezer_action_supported_by_children(Unit *s) {
351 Unit *member;
352 int r;
353
354 assert(s);
355
356 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
357
358 if (member->type == UNIT_SLICE) {
359 r = slice_freezer_action_supported_by_children(member);
360 if (!r)
361 return r;
362 }
363
364 if (!UNIT_VTABLE(member)->freeze)
365 return false;
366 }
367
368 return true;
369 }
370
slice_freezer_action(Unit * s,FreezerAction action)371 static int slice_freezer_action(Unit *s, FreezerAction action) {
372 Unit *member;
373 int r;
374
375 assert(s);
376 assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
377
378 if (!slice_freezer_action_supported_by_children(s)) {
379 log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
380 return 0;
381 }
382
383 UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
384 if (action == FREEZER_FREEZE)
385 r = UNIT_VTABLE(member)->freeze(member);
386 else
387 r = UNIT_VTABLE(member)->thaw(member);
388 if (r < 0)
389 return r;
390 }
391
392 return unit_cgroup_freezer_action(s, action);
393 }
394
slice_freeze(Unit * s)395 static int slice_freeze(Unit *s) {
396 assert(s);
397
398 return slice_freezer_action(s, FREEZER_FREEZE);
399 }
400
slice_thaw(Unit * s)401 static int slice_thaw(Unit *s) {
402 assert(s);
403
404 return slice_freezer_action(s, FREEZER_THAW);
405 }
406
slice_can_freeze(Unit * s)407 static bool slice_can_freeze(Unit *s) {
408 assert(s);
409
410 return slice_freezer_action_supported_by_children(s);
411 }
412
413 const UnitVTable slice_vtable = {
414 .object_size = sizeof(Slice),
415 .cgroup_context_offset = offsetof(Slice, cgroup_context),
416
417 .sections =
418 "Unit\0"
419 "Slice\0"
420 "Install\0",
421 .private_section = "Slice",
422
423 .can_transient = true,
424 .can_set_managed_oom = true,
425
426 .init = slice_init,
427 .load = slice_load,
428
429 .coldplug = slice_coldplug,
430
431 .dump = slice_dump,
432
433 .start = slice_start,
434 .stop = slice_stop,
435
436 .kill = slice_kill,
437
438 .freeze = slice_freeze,
439 .thaw = slice_thaw,
440 .can_freeze = slice_can_freeze,
441
442 .serialize = slice_serialize,
443 .deserialize_item = slice_deserialize_item,
444
445 .active_state = slice_active_state,
446 .sub_state_to_string = slice_sub_state_to_string,
447
448 .bus_set_property = bus_slice_set_property,
449 .bus_commit_properties = bus_slice_commit_properties,
450
451 .enumerate_perpetual = slice_enumerate_perpetual,
452
453 .status_message_formats = {
454 .finished_start_job = {
455 [JOB_DONE] = "Created slice %s.",
456 },
457 .finished_stop_job = {
458 [JOB_DONE] = "Removed slice %s.",
459 },
460 },
461 };
462