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 19 : static void slice_init(Unit *u) {
23 19 : assert(u);
24 19 : assert(u->load_state == UNIT_STUB);
25 :
26 19 : u->ignore_on_isolate = true;
27 19 : }
28 :
29 11 : static void slice_set_state(Slice *t, SliceState state) {
30 : SliceState old_state;
31 11 : assert(t);
32 :
33 11 : if (t->state != state)
34 11 : bus_unit_send_pending_change_signal(UNIT(t), false);
35 :
36 11 : old_state = t->state;
37 11 : t->state = state;
38 :
39 11 : if (state != old_state)
40 11 : 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 11 : unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
46 11 : }
47 :
48 18 : static int slice_add_parent_slice(Slice *s) {
49 18 : Unit *u = UNIT(s), *parent;
50 18 : _cleanup_free_ char *a = NULL;
51 : int r;
52 :
53 18 : assert(s);
54 :
55 18 : if (UNIT_ISSET(u->slice))
56 0 : return 0;
57 :
58 18 : r = slice_build_parent_slice(u->id, &a);
59 18 : if (r <= 0) /* 0 means root slice */
60 11 : return r;
61 :
62 7 : r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
63 7 : if (r < 0)
64 0 : return r;
65 :
66 7 : unit_ref_set(&u->slice, u, parent);
67 7 : return 0;
68 : }
69 :
70 18 : static int slice_add_default_dependencies(Slice *s) {
71 : int r;
72 :
73 18 : assert(s);
74 :
75 18 : if (!UNIT(s)->default_dependencies)
76 11 : return 0;
77 :
78 : /* Make sure slices are unloaded on shutdown */
79 7 : r = unit_add_two_dependencies_by_name(
80 7 : UNIT(s),
81 : UNIT_BEFORE, UNIT_CONFLICTS,
82 : SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
83 7 : if (r < 0)
84 0 : return r;
85 :
86 7 : return 0;
87 : }
88 :
89 18 : static int slice_verify(Slice *s) {
90 18 : _cleanup_free_ char *parent = NULL;
91 : int r;
92 :
93 18 : assert(s);
94 :
95 18 : if (UNIT(s)->load_state != UNIT_LOADED)
96 0 : return 0;
97 :
98 18 : 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 18 : r = slice_build_parent_slice(UNIT(s)->id, &parent);
104 18 : if (r < 0)
105 0 : return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
106 :
107 18 : 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 18 : return 0;
113 : }
114 :
115 18 : static int slice_load_root_slice(Unit *u) {
116 18 : assert(u);
117 :
118 18 : if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
119 7 : return 0;
120 :
121 11 : 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 11 : u->default_dependencies = false;
127 :
128 11 : if (!u->description)
129 11 : u->description = strdup("Root Slice");
130 11 : if (!u->documentation)
131 11 : u->documentation = strv_new("man:systemd.special(7)");
132 :
133 11 : return 1;
134 : }
135 :
136 18 : static int slice_load_system_slice(Unit *u) {
137 18 : assert(u);
138 :
139 18 : if (!MANAGER_IS_SYSTEM(u->manager))
140 18 : 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 18 : static int slice_load(Unit *u) {
160 18 : Slice *s = SLICE(u);
161 : int r;
162 :
163 18 : assert(s);
164 18 : assert(u->load_state == UNIT_STUB);
165 :
166 18 : r = slice_load_root_slice(u);
167 18 : if (r < 0)
168 0 : return r;
169 18 : r = slice_load_system_slice(u);
170 18 : if (r < 0)
171 0 : return r;
172 :
173 18 : r = unit_load_fragment_and_dropin_optional(u);
174 18 : if (r < 0)
175 0 : return r;
176 :
177 : /* This is a new unit? Then let's add in some extras */
178 18 : if (u->load_state == UNIT_LOADED) {
179 :
180 18 : r = unit_patch_contexts(u);
181 18 : if (r < 0)
182 0 : return r;
183 :
184 18 : r = slice_add_parent_slice(s);
185 18 : if (r < 0)
186 0 : return r;
187 :
188 18 : r = slice_add_default_dependencies(s);
189 18 : if (r < 0)
190 0 : return r;
191 : }
192 :
193 18 : return slice_verify(s);
194 : }
195 :
196 11 : static int slice_coldplug(Unit *u) {
197 11 : Slice *t = SLICE(u);
198 :
199 11 : assert(t);
200 11 : assert(t->state == SLICE_DEAD);
201 :
202 11 : if (t->deserialized_state != t->state)
203 11 : slice_set_state(t, t->deserialized_state);
204 :
205 11 : return 0;
206 : }
207 :
208 6 : static void slice_dump(Unit *u, FILE *f, const char *prefix) {
209 6 : Slice *t = SLICE(u);
210 :
211 6 : assert(t);
212 6 : assert(f);
213 :
214 6 : fprintf(f,
215 : "%sSlice State: %s\n",
216 : prefix, slice_state_to_string(t->state));
217 :
218 6 : cgroup_context_dump(&t->cgroup_context, f, prefix);
219 6 : }
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 880 : _pure_ static UnitActiveState slice_active_state(Unit *u) {
292 880 : assert(u);
293 :
294 880 : 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 11 : static int slice_make_perpetual(Manager *m, const char *name, Unit **ret) {
304 : Unit *u;
305 : int r;
306 :
307 11 : assert(m);
308 11 : assert(name);
309 :
310 11 : u = manager_get_unit(m, name);
311 11 : if (!u) {
312 11 : r = unit_new_for_name(m, sizeof(Slice), name, &u);
313 11 : if (r < 0)
314 0 : return log_error_errno(r, "Failed to allocate the special %s unit: %m", name);
315 : }
316 :
317 11 : u->perpetual = true;
318 11 : SLICE(u)->deserialized_state = SLICE_ACTIVE;
319 :
320 11 : unit_add_to_load_queue(u);
321 11 : unit_add_to_dbus_queue(u);
322 :
323 11 : if (ret)
324 11 : *ret = u;
325 :
326 11 : return 0;
327 : }
328 :
329 11 : static void slice_enumerate_perpetual(Manager *m) {
330 : Unit *u;
331 : int r;
332 :
333 11 : assert(m);
334 :
335 11 : r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
336 11 : 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 11 : if (MANAGER_IS_SYSTEM(m))
349 0 : (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
350 11 : }
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 : };
|