]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/slice.c
Merge pull request #7661 from keszybz/slice-cleanups
[thirdparty/systemd.git] / src / core / slice.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22
23 #include "alloc-util.h"
24 #include "dbus-slice.h"
25 #include "log.h"
26 #include "slice.h"
27 #include "special.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "unit-name.h"
31 #include "unit.h"
32
33 static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
34 [SLICE_DEAD] = UNIT_INACTIVE,
35 [SLICE_ACTIVE] = UNIT_ACTIVE
36 };
37
38 static void slice_init(Unit *u) {
39 assert(u);
40 assert(u->load_state == UNIT_STUB);
41
42 u->ignore_on_isolate = true;
43 }
44
45 static void slice_set_state(Slice *t, SliceState state) {
46 SliceState old_state;
47 assert(t);
48
49 old_state = t->state;
50 t->state = state;
51
52 if (state != old_state)
53 log_debug("%s changed %s -> %s",
54 UNIT(t)->id,
55 slice_state_to_string(old_state),
56 slice_state_to_string(state));
57
58 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
59 }
60
61 static int slice_add_parent_slice(Slice *s) {
62 Unit *u = UNIT(s), *parent;
63 _cleanup_free_ char *a = NULL;
64 int r;
65
66 assert(s);
67
68 if (UNIT_ISSET(u->slice))
69 return 0;
70
71 r = slice_build_parent_slice(u->id, &a);
72 if (r <= 0) /* 0 means root slice */
73 return r;
74
75 r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
76 if (r < 0)
77 return r;
78
79 unit_ref_set(&u->slice, parent);
80 return 0;
81 }
82
83 static int slice_add_default_dependencies(Slice *s) {
84 int r;
85
86 assert(s);
87
88 if (!UNIT(s)->default_dependencies)
89 return 0;
90
91 /* Make sure slices are unloaded on shutdown */
92 r = unit_add_two_dependencies_by_name(
93 UNIT(s),
94 UNIT_BEFORE, UNIT_CONFLICTS,
95 SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
96 if (r < 0)
97 return r;
98
99 return 0;
100 }
101
102 static int slice_verify(Slice *s) {
103 _cleanup_free_ char *parent = NULL;
104 int r;
105
106 assert(s);
107
108 if (UNIT(s)->load_state != UNIT_LOADED)
109 return 0;
110
111 if (!slice_name_is_valid(UNIT(s)->id)) {
112 log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
113 return -EINVAL;
114 }
115
116 r = slice_build_parent_slice(UNIT(s)->id, &parent);
117 if (r < 0)
118 return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
119
120 if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) {
121 log_unit_error(UNIT(s), "Located outside of parent slice. Refusing.");
122 return -EINVAL;
123 }
124
125 return 0;
126 }
127
128 static int slice_load_root_slice(Unit *u) {
129 assert(u);
130
131 if (!unit_has_name(u, SPECIAL_ROOT_SLICE))
132 return 0;
133
134 u->perpetual = true;
135
136 /* The root 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 u->ignore_on_isolate = true;
141
142 if (!u->description)
143 u->description = strdup("Root Slice");
144 if (!u->documentation)
145 u->documentation = strv_new("man:systemd.special(7)", NULL);
146
147 return 1;
148 }
149
150 static int slice_load(Unit *u) {
151 Slice *s = SLICE(u);
152 int r;
153
154 assert(s);
155 assert(u->load_state == UNIT_STUB);
156
157 r = slice_load_root_slice(u);
158 if (r < 0)
159 return r;
160 r = unit_load_fragment_and_dropin_optional(u);
161 if (r < 0)
162 return r;
163
164 /* This is a new unit? Then let's add in some extras */
165 if (u->load_state == UNIT_LOADED) {
166
167 r = unit_patch_contexts(u);
168 if (r < 0)
169 return r;
170
171 r = slice_add_parent_slice(s);
172 if (r < 0)
173 return r;
174
175 r = slice_add_default_dependencies(s);
176 if (r < 0)
177 return r;
178 }
179
180 return slice_verify(s);
181 }
182
183 static int slice_coldplug(Unit *u) {
184 Slice *t = SLICE(u);
185
186 assert(t);
187 assert(t->state == SLICE_DEAD);
188
189 if (t->deserialized_state != t->state)
190 slice_set_state(t, t->deserialized_state);
191
192 return 0;
193 }
194
195 static void slice_dump(Unit *u, FILE *f, const char *prefix) {
196 Slice *t = SLICE(u);
197
198 assert(t);
199 assert(f);
200
201 fprintf(f,
202 "%sSlice State: %s\n",
203 prefix, slice_state_to_string(t->state));
204
205 cgroup_context_dump(&t->cgroup_context, f, prefix);
206 }
207
208 static int slice_start(Unit *u) {
209 Slice *t = SLICE(u);
210 int r;
211
212 assert(t);
213 assert(t->state == SLICE_DEAD);
214
215 r = unit_acquire_invocation_id(u);
216 if (r < 0)
217 return r;
218
219 (void) unit_realize_cgroup(u);
220 (void) unit_reset_cpu_accounting(u);
221 (void) unit_reset_ip_accounting(u);
222
223 slice_set_state(t, SLICE_ACTIVE);
224 return 1;
225 }
226
227 static int slice_stop(Unit *u) {
228 Slice *t = SLICE(u);
229
230 assert(t);
231 assert(t->state == SLICE_ACTIVE);
232
233 /* We do not need to destroy the cgroup explicitly,
234 * unit_notify() will do that for us anyway. */
235
236 slice_set_state(t, SLICE_DEAD);
237 return 1;
238 }
239
240 static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
241 return unit_kill_common(u, who, signo, -1, -1, error);
242 }
243
244 static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
245 Slice *s = SLICE(u);
246
247 assert(s);
248 assert(f);
249 assert(fds);
250
251 unit_serialize_item(u, f, "state", slice_state_to_string(s->state));
252 return 0;
253 }
254
255 static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
256 Slice *s = SLICE(u);
257
258 assert(u);
259 assert(key);
260 assert(value);
261 assert(fds);
262
263 if (streq(key, "state")) {
264 SliceState state;
265
266 state = slice_state_from_string(value);
267 if (state < 0)
268 log_debug("Failed to parse state value %s", value);
269 else
270 s->deserialized_state = state;
271
272 } else
273 log_debug("Unknown serialization key '%s'", key);
274
275 return 0;
276 }
277
278 _pure_ static UnitActiveState slice_active_state(Unit *u) {
279 assert(u);
280
281 return state_translation_table[SLICE(u)->state];
282 }
283
284 _pure_ static const char *slice_sub_state_to_string(Unit *u) {
285 assert(u);
286
287 return slice_state_to_string(SLICE(u)->state);
288 }
289
290 static void slice_enumerate(Manager *m) {
291 Unit *u;
292 int r;
293
294 assert(m);
295
296 u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
297 if (!u) {
298 r = unit_new_for_name(m, sizeof(Slice), SPECIAL_ROOT_SLICE, &u);
299 if (r < 0) {
300 log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_SLICE " unit: %m");
301 return;
302 }
303 }
304
305 u->perpetual = true;
306 SLICE(u)->deserialized_state = SLICE_ACTIVE;
307
308 unit_add_to_load_queue(u);
309 unit_add_to_dbus_queue(u);
310 }
311
312 const UnitVTable slice_vtable = {
313 .object_size = sizeof(Slice),
314 .cgroup_context_offset = offsetof(Slice, cgroup_context),
315
316 .sections =
317 "Unit\0"
318 "Slice\0"
319 "Install\0",
320 .private_section = "Slice",
321
322 .can_transient = true,
323
324 .init = slice_init,
325 .load = slice_load,
326
327 .coldplug = slice_coldplug,
328
329 .dump = slice_dump,
330
331 .start = slice_start,
332 .stop = slice_stop,
333
334 .kill = slice_kill,
335
336 .serialize = slice_serialize,
337 .deserialize_item = slice_deserialize_item,
338
339 .active_state = slice_active_state,
340 .sub_state_to_string = slice_sub_state_to_string,
341
342 .bus_vtable = bus_slice_vtable,
343 .bus_set_property = bus_slice_set_property,
344 .bus_commit_properties = bus_slice_commit_properties,
345
346 .enumerate = slice_enumerate,
347
348 .status_message_formats = {
349 .finished_start_job = {
350 [JOB_DONE] = "Created slice %s.",
351 },
352 .finished_stop_job = {
353 [JOB_DONE] = "Removed slice %s.",
354 },
355 },
356 };