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