Branch data Line data Source code
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 : 76 : static void slice_init(Unit *u) {
23 [ - + ]: 76 : assert(u);
24 [ - + ]: 76 : assert(u->load_state == UNIT_STUB);
25 : :
26 : 76 : u->ignore_on_isolate = true;
27 : 76 : }
28 : :
29 : 44 : static void slice_set_state(Slice *t, SliceState state) {
30 : : SliceState old_state;
31 [ - + ]: 44 : assert(t);
32 : :
33 [ + - ]: 44 : if (t->state != state)
34 [ + - ]: 44 : bus_unit_send_pending_change_signal(UNIT(t), false);
35 : :
36 : 44 : old_state = t->state;
37 : 44 : t->state = state;
38 : :
39 [ + - ]: 44 : if (state != old_state)
40 [ + + + - ]: 44 : log_debug("%s changed %s -> %s",
41 : : UNIT(t)->id,
42 : : slice_state_to_string(old_state),
43 : : slice_state_to_string(state));
44 : :
45 [ + - ]: 44 : unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
46 : 44 : }
47 : :
48 : 72 : static int slice_add_parent_slice(Slice *s) {
49 [ + - ]: 72 : Unit *u = UNIT(s), *parent;
50 : 72 : _cleanup_free_ char *a = NULL;
51 : : int r;
52 : :
53 [ - + ]: 72 : assert(s);
54 : :
55 [ - + ]: 72 : if (UNIT_ISSET(u->slice))
56 : 0 : return 0;
57 : :
58 : 72 : r = slice_build_parent_slice(u->id, &a);
59 [ + + ]: 72 : if (r <= 0) /* 0 means root slice */
60 : 44 : return r;
61 : :
62 : 28 : r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
63 [ - + ]: 28 : if (r < 0)
64 : 0 : return r;
65 : :
66 : 28 : unit_ref_set(&u->slice, u, parent);
67 : 28 : return 0;
68 : : }
69 : :
70 : 72 : static int slice_add_default_dependencies(Slice *s) {
71 : : int r;
72 : :
73 [ - + ]: 72 : assert(s);
74 : :
75 [ + - + + ]: 72 : if (!UNIT(s)->default_dependencies)
76 : 44 : return 0;
77 : :
78 : : /* Make sure slices are unloaded on shutdown */
79 : 28 : r = unit_add_two_dependencies_by_name(
80 [ + - ]: 28 : UNIT(s),
81 : : UNIT_BEFORE, UNIT_CONFLICTS,
82 : : SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
83 [ - + ]: 28 : if (r < 0)
84 : 0 : return r;
85 : :
86 : 28 : return 0;
87 : : }
88 : :
89 : 72 : static int slice_verify(Slice *s) {
90 : 72 : _cleanup_free_ char *parent = NULL;
91 : : int r;
92 : :
93 [ - + ]: 72 : assert(s);
94 : :
95 [ + - - + ]: 72 : if (UNIT(s)->load_state != UNIT_LOADED)
96 : 0 : return 0;
97 : :
98 [ + - - + ]: 72 : 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 : : }
102 : :
103 [ + - ]: 72 : r = slice_build_parent_slice(UNIT(s)->id, &parent);
104 [ - + ]: 72 : if (r < 0)
105 [ # # # # ]: 0 : return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
106 : :
107 [ + + + - : 72 : 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 : : }
111 : :
112 : 72 : return 0;
113 : : }
114 : :
115 : 72 : static int slice_load_root_slice(Unit *u) {
116 [ - + ]: 72 : assert(u);
117 : :
118 [ + + ]: 72 : if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
119 : 28 : return 0;
120 : :
121 : 44 : u->perpetual = true;
122 : :
123 : : /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its
124 : : * special semantics we synthesize it here, instead of relying on the unit file on disk. */
125 : :
126 : 44 : u->default_dependencies = false;
127 : :
128 [ + - ]: 44 : if (!u->description)
129 : 44 : u->description = strdup("Root Slice");
130 [ + - ]: 44 : if (!u->documentation)
131 : 44 : u->documentation = strv_new("man:systemd.special(7)");
132 : :
133 : 44 : return 1;
134 : : }
135 : :
136 : 72 : static int slice_load_system_slice(Unit *u) {
137 [ - + ]: 72 : assert(u);
138 : :
139 [ + - ]: 72 : if (!MANAGER_IS_SYSTEM(u->manager))
140 : 72 : return 0;
141 [ # # ]: 0 : if (!unit_has_name(u, SPECIAL_SYSTEM_SLICE))
142 : 0 : return 0;
143 : :
144 : 0 : u->perpetual = true;
145 : :
146 : : /* The system slice is a bit special. For example it is always running and cannot be terminated. Because of its
147 : : * special semantics we synthesize it here, instead of relying on the unit file on disk. */
148 : :
149 : 0 : u->default_dependencies = false;
150 : :
151 [ # # ]: 0 : if (!u->description)
152 : 0 : u->description = strdup("System Slice");
153 [ # # ]: 0 : if (!u->documentation)
154 : 0 : u->documentation = strv_new("man:systemd.special(7)");
155 : :
156 : 0 : return 1;
157 : : }
158 : :
159 : 72 : static int slice_load(Unit *u) {
160 : 72 : Slice *s = SLICE(u);
161 : : int r;
162 : :
163 [ - + ]: 72 : assert(s);
164 [ - + ]: 72 : assert(u->load_state == UNIT_STUB);
165 : :
166 : 72 : r = slice_load_root_slice(u);
167 [ - + ]: 72 : if (r < 0)
168 : 0 : return r;
169 : 72 : r = slice_load_system_slice(u);
170 [ - + ]: 72 : if (r < 0)
171 : 0 : return r;
172 : :
173 : 72 : r = unit_load_fragment_and_dropin_optional(u);
174 [ - + ]: 72 : if (r < 0)
175 : 0 : return r;
176 : :
177 : : /* This is a new unit? Then let's add in some extras */
178 [ + - ]: 72 : if (u->load_state == UNIT_LOADED) {
179 : :
180 : 72 : r = unit_patch_contexts(u);
181 [ - + ]: 72 : if (r < 0)
182 : 0 : return r;
183 : :
184 : 72 : r = slice_add_parent_slice(s);
185 [ - + ]: 72 : if (r < 0)
186 : 0 : return r;
187 : :
188 : 72 : r = slice_add_default_dependencies(s);
189 [ - + ]: 72 : if (r < 0)
190 : 0 : return r;
191 : : }
192 : :
193 : 72 : return slice_verify(s);
194 : : }
195 : :
196 : 44 : static int slice_coldplug(Unit *u) {
197 : 44 : Slice *t = SLICE(u);
198 : :
199 [ - + ]: 44 : assert(t);
200 [ - + ]: 44 : assert(t->state == SLICE_DEAD);
201 : :
202 [ + - ]: 44 : if (t->deserialized_state != t->state)
203 : 44 : slice_set_state(t, t->deserialized_state);
204 : :
205 : 44 : return 0;
206 : : }
207 : :
208 : 24 : static void slice_dump(Unit *u, FILE *f, const char *prefix) {
209 : 24 : Slice *t = SLICE(u);
210 : :
211 [ - + ]: 24 : assert(t);
212 [ - + ]: 24 : assert(f);
213 : :
214 : 24 : fprintf(f,
215 : : "%sSlice State: %s\n",
216 : : prefix, slice_state_to_string(t->state));
217 : :
218 : 24 : cgroup_context_dump(&t->cgroup_context, f, prefix);
219 : 24 : }
220 : :
221 : 0 : static int slice_start(Unit *u) {
222 : 0 : Slice *t = SLICE(u);
223 : : int r;
224 : :
225 [ # # ]: 0 : assert(t);
226 [ # # ]: 0 : assert(t->state == SLICE_DEAD);
227 : :
228 : 0 : r = unit_acquire_invocation_id(u);
229 [ # # ]: 0 : if (r < 0)
230 : 0 : return r;
231 : :
232 : 0 : (void) unit_realize_cgroup(u);
233 : 0 : (void) unit_reset_accounting(u);
234 : :
235 : 0 : slice_set_state(t, SLICE_ACTIVE);
236 : 0 : return 1;
237 : : }
238 : :
239 : 0 : static int slice_stop(Unit *u) {
240 : 0 : Slice *t = SLICE(u);
241 : :
242 [ # # ]: 0 : assert(t);
243 [ # # ]: 0 : assert(t->state == SLICE_ACTIVE);
244 : :
245 : : /* We do not need to destroy the cgroup explicitly,
246 : : * unit_notify() will do that for us anyway. */
247 : :
248 : 0 : slice_set_state(t, SLICE_DEAD);
249 : 0 : return 1;
250 : : }
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 : : }
255 : :
256 : 0 : static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
257 : 0 : Slice *s = SLICE(u);
258 : :
259 [ # # ]: 0 : assert(s);
260 [ # # ]: 0 : assert(f);
261 [ # # ]: 0 : assert(fds);
262 : :
263 : 0 : (void) serialize_item(f, "state", slice_state_to_string(s->state));
264 : :
265 : 0 : return 0;
266 : : }
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 : :
271 [ # # ]: 0 : assert(u);
272 [ # # ]: 0 : assert(key);
273 [ # # ]: 0 : assert(value);
274 [ # # ]: 0 : assert(fds);
275 : :
276 [ # # ]: 0 : if (streq(key, "state")) {
277 : : SliceState state;
278 : :
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 : : else
283 : 0 : s->deserialized_state = state;
284 : :
285 : : } else
286 [ # # ]: 0 : log_debug("Unknown serialization key '%s'", key);
287 : :
288 : 0 : return 0;
289 : : }
290 : :
291 : 2956 : _pure_ static UnitActiveState slice_active_state(Unit *u) {
292 [ - + ]: 2956 : assert(u);
293 : :
294 : 2956 : return state_translation_table[SLICE(u)->state];
295 : : }
296 : :
297 : 0 : _pure_ static const char *slice_sub_state_to_string(Unit *u) {
298 [ # # ]: 0 : assert(u);
299 : :
300 : 0 : return slice_state_to_string(SLICE(u)->state);
301 : : }
302 : :
303 : 44 : static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
304 : : Unit *u;
305 : : int r;
306 : :
307 [ - + ]: 44 : assert(m);
308 [ - + ]: 44 : assert(name);
309 : :
310 : 44 : u = manager_get_unit(m, name);
311 [ + - ]: 44 : if (!u) {
312 : 44 : r = unit_new_for_name(m, sizeof(Slice), name, &u);
313 [ - + ]: 44 : if (r < 0)
314 [ # # ]: 0 : return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
315 : : }
316 : :
317 : 44 : u->perpetual = true;
318 : 44 : SLICE(u)->deserialized_state = SLICE_ACTIVE;
319 : :
320 : 44 : unit_add_to_load_queue(u);
321 : 44 : unit_add_to_dbus_queue(u);
322 : :
323 [ + - ]: 44 : if (ret)
324 : 44 : *ret = u;
325 : :
326 : 44 : return 0;
327 : : }
328 : :
329 : 44 : static void slice_enumerate_perpetual(Manager *m) {
330 : : Unit *u;
331 : : int r;
332 : :
333 [ - + ]: 44 : assert(m);
334 : :
335 : 44 : r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
336 [ + - - + ]: 44 : if (r >= 0 && manager_owns_host_root_cgroup(m)) {
337 : 0 : Slice *s = SLICE(u);
338 : :
339 : : /* If we are managing the root cgroup then this means our root slice covers the whole system, which
340 : : * means the kernel will track CPU/tasks/memory for us anyway, and it is all available in /proc. Let's
341 : : * hence turn accounting on here, so that our APIs to query this data are available. */
342 : :
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 : : }
347 : :
348 [ - + ]: 44 : if (MANAGER_IS_SYSTEM(m))
349 : 0 : (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
350 : 44 : }
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 : : };
|