]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/scope.c
core: add new "scope" unit type for making a unit of pre-existing processes
[thirdparty/systemd.git] / src / core / scope.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <signal.h>
24 #include <unistd.h>
25
26 #include "unit.h"
27 #include "scope.h"
28 #include "load-fragment.h"
29 #include "log.h"
30 #include "dbus-scope.h"
31 #include "special.h"
32 #include "unit-name.h"
33 #include "load-dropin.h"
34
35 static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
36 [SCOPE_DEAD] = UNIT_INACTIVE,
37 [SCOPE_RUNNING] = UNIT_ACTIVE,
38 [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
39 [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
40 [SCOPE_FAILED] = UNIT_FAILED
41 };
42
43 static void scope_init(Unit *u) {
44 Scope *s = SCOPE(u);
45
46 assert(u);
47 assert(u->load_state == UNIT_STUB);
48
49 s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
50
51 watch_init(&s->timer_watch);
52
53 cgroup_context_init(&s->cgroup_context);
54 kill_context_init(&s->kill_context);
55
56 UNIT(s)->ignore_on_isolate = true;
57 UNIT(s)->ignore_on_snapshot = true;
58 }
59
60 static void scope_done(Unit *u) {
61 Scope *s = SCOPE(u);
62
63 assert(u);
64
65 cgroup_context_done(&s->cgroup_context);
66
67 set_free(s->pids);
68 s->pids = NULL;
69
70 unit_unwatch_timer(u, &s->timer_watch);
71 }
72
73 static void scope_set_state(Scope *s, ScopeState state) {
74 ScopeState old_state;
75 assert(s);
76
77 old_state = s->state;
78 s->state = state;
79
80 if (state != SCOPE_STOP_SIGTERM &&
81 state != SCOPE_STOP_SIGKILL)
82 unit_unwatch_timer(UNIT(s), &s->timer_watch);
83
84 if (state != old_state)
85 log_debug("%s changed %s -> %s",
86 UNIT(s)->id,
87 scope_state_to_string(old_state),
88 scope_state_to_string(state));
89
90 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
91 }
92
93 static int scope_add_default_dependencies(Scope *s) {
94 int r;
95
96 assert(s);
97
98 /* Make sure scopes are unloaded on shutdown */
99 r = unit_add_two_dependencies_by_name(
100 UNIT(s),
101 UNIT_BEFORE, UNIT_CONFLICTS,
102 SPECIAL_SHUTDOWN_TARGET, NULL, true);
103 if (r < 0)
104 return r;
105
106 return 0;
107 }
108
109 static int scope_verify(Scope *s) {
110 assert(s);
111
112 if (UNIT(s)->load_state != UNIT_LOADED)
113 return 0;
114
115 if (set_size(s->pids) <= 0) {
116 log_error_unit(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id);
117 return -EINVAL;
118 }
119
120 return 0;
121 }
122
123 static int scope_load(Unit *u) {
124 Scope *s = SCOPE(u);
125 int r;
126
127 assert(s);
128 assert(u->load_state == UNIT_STUB);
129
130 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
131 return -ENOENT;
132
133 u->load_state = UNIT_LOADED;
134
135 r = unit_load_dropin(u);
136 if (r < 0)
137 return r;
138
139 r = unit_add_default_slice(u);
140 if (r < 0)
141 return r;
142
143 if (u->default_dependencies) {
144 r = scope_add_default_dependencies(s);
145 if (r < 0)
146 return r;
147 }
148
149 return scope_verify(s);
150 }
151
152 static int scope_coldplug(Unit *u) {
153 Scope *s = SCOPE(u);
154 int r;
155
156 assert(s);
157 assert(s->state == SCOPE_DEAD);
158
159 if (s->deserialized_state != s->state) {
160
161 if ((s->deserialized_state == SCOPE_STOP_SIGKILL || s->deserialized_state == SCOPE_STOP_SIGTERM)
162 && s->timeout_stop_usec > 0) {
163 r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
164 if (r < 0)
165
166 return r;
167 }
168
169 scope_set_state(s, s->deserialized_state);
170 }
171
172 return 0;
173 }
174
175 static void scope_dump(Unit *u, FILE *f, const char *prefix) {
176 Scope *s = SCOPE(u);
177
178 assert(s);
179 assert(f);
180
181 fprintf(f,
182 "%sScope State: %s\n"
183 "%sResult: %s\n",
184 prefix, scope_state_to_string(s->state),
185 prefix, scope_result_to_string(s->result));
186
187 cgroup_context_dump(&s->cgroup_context, f, prefix);
188 kill_context_dump(&s->kill_context, f, prefix);
189 }
190
191 static void scope_enter_dead(Scope *s, ScopeResult f) {
192 assert(s);
193
194 if (f != SCOPE_SUCCESS)
195 s->result = f;
196
197 scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
198 }
199
200 static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
201 int r;
202
203 assert(s);
204
205 if (f != SCOPE_SUCCESS)
206 s->result = f;
207
208 r = unit_kill_context(
209 UNIT(s),
210 &s->kill_context,
211 state != SCOPE_STOP_SIGTERM,
212 -1, -1, false);
213 if (r < 0)
214 goto fail;
215
216 if (r > 0) {
217 if (s->timeout_stop_usec > 0) {
218 r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
219 if (r < 0)
220 goto fail;
221 }
222
223 scope_set_state(s, state);
224 } else
225 scope_enter_dead(s, SCOPE_SUCCESS);
226
227 return;
228
229 fail:
230 log_warning_unit(UNIT(s)->id,
231 "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
232
233 scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
234 }
235
236 static int scope_start(Unit *u) {
237 Scope *s = SCOPE(u);
238 int r;
239
240 assert(s);
241
242 if (s->state == SCOPE_STOP_SIGTERM ||
243 s->state == SCOPE_STOP_SIGKILL)
244 return -EAGAIN;
245
246 assert(s->state == SCOPE_DEAD);
247
248 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
249 return -ENOENT;
250
251 r = unit_realize_cgroup(u);
252 if (r < 0) {
253 log_error("Failed to realize cgroup: %s", strerror(-r));
254 return r;
255 }
256
257 r = cg_attach_many_with_mask(u->cgroup_mask, u->cgroup_path, s->pids);
258 if (r < 0)
259 return r;
260
261 set_free(s->pids);
262 s->pids = NULL;
263
264 s->result = SCOPE_SUCCESS;
265
266 scope_set_state(s, SCOPE_RUNNING);
267 return 0;
268 }
269
270 static int scope_stop(Unit *u) {
271 Scope *s = SCOPE(u);
272
273 assert(s);
274 assert(s->state == SCOPE_RUNNING);
275
276 if (s->state == SCOPE_STOP_SIGTERM ||
277 s->state == SCOPE_STOP_SIGKILL)
278 return 0;
279
280 assert(s->state == SCOPE_RUNNING);
281
282 scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
283 return 0;
284 }
285
286 static int scope_kill(Unit *u, KillWho who, int signo, DBusError *error) {
287 return unit_kill_common(u, who, signo, -1, -1, error);
288 }
289
290 static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
291 Scope *s = SCOPE(u);
292
293 assert(s);
294 assert(f);
295 assert(fds);
296
297 unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
298 return 0;
299 }
300
301 static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
302 Scope *s = SCOPE(u);
303
304 assert(u);
305 assert(key);
306 assert(value);
307 assert(fds);
308
309 if (streq(key, "state")) {
310 ScopeState state;
311
312 state = scope_state_from_string(value);
313 if (state < 0)
314 log_debug("Failed to parse state value %s", value);
315 else
316 s->deserialized_state = state;
317
318 } else
319 log_debug("Unknown serialization key '%s'", key);
320
321 return 0;
322 }
323
324 static bool scope_check_gc(Unit *u) {
325 Scope *s = SCOPE(u);
326 int r;
327
328 assert(s);
329
330 /* Never clean up scopes that still have a process around,
331 * even if the scope is formally dead. */
332
333 if (UNIT(s)->cgroup_path) {
334 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
335 if (r <= 0)
336 return true;
337 }
338
339 return false;
340 }
341
342 static void scope_timer_event(Unit *u, uint64_t elapsed, Watch*w) {
343 Scope *s = SCOPE(u);
344
345 assert(s);
346 assert(elapsed == 1);
347 assert(w == &s->timer_watch);
348
349 switch (s->state) {
350
351 case SCOPE_STOP_SIGTERM:
352 if (s->kill_context.send_sigkill) {
353 log_warning_unit(u->id, "%s stopping timed out. Killing.", u->id);
354 scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
355 } else {
356 log_warning_unit(u->id, "%s stopping timed out. Skipping SIGKILL.", u->id);
357 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
358 }
359
360 break;
361
362 case SCOPE_STOP_SIGKILL:
363 log_warning_unit(u->id, "%s still around after SIGKILL. Ignoring.", u->id);
364 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
365 break;
366
367 default:
368 assert_not_reached("Timeout at wrong time.");
369 }
370 }
371
372 static void scope_notify_cgroup_empty_event(Unit *u) {
373 Scope *s = SCOPE(u);
374 assert(u);
375
376 log_debug_unit(u->id, "%s: cgroup is empty", u->id);
377
378 switch (s->state) {
379
380 case SCOPE_RUNNING:
381 case SCOPE_STOP_SIGTERM:
382 case SCOPE_STOP_SIGKILL:
383 scope_enter_dead(s, SCOPE_SUCCESS);
384
385 break;
386
387 default:
388 ;
389 }
390 }
391
392 _pure_ static UnitActiveState scope_active_state(Unit *u) {
393 assert(u);
394
395 return state_translation_table[SCOPE(u)->state];
396 }
397
398 _pure_ static const char *scope_sub_state_to_string(Unit *u) {
399 assert(u);
400
401 return scope_state_to_string(SCOPE(u)->state);
402 }
403
404 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
405 [SCOPE_DEAD] = "dead",
406 [SCOPE_RUNNING] = "active",
407 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
408 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
409 [SCOPE_FAILED] = "failed",
410 };
411
412 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
413
414 static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
415 [SCOPE_SUCCESS] = "success",
416 [SCOPE_FAILURE_RESOURCES] = "resources",
417 [SCOPE_FAILURE_TIMEOUT] = "timeout",
418 };
419
420 DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
421
422 const UnitVTable scope_vtable = {
423 .object_size = sizeof(Scope),
424 .sections =
425 "Unit\0"
426 "Scope\0"
427 "Install\0",
428
429 .private_section = "Scope",
430 .cgroup_context_offset = offsetof(Scope, cgroup_context),
431
432 .no_alias = true,
433 .no_instances = true,
434
435 .init = scope_init,
436 .load = scope_load,
437 .done = scope_done,
438
439 .coldplug = scope_coldplug,
440
441 .dump = scope_dump,
442
443 .start = scope_start,
444 .stop = scope_stop,
445
446 .kill = scope_kill,
447
448 .serialize = scope_serialize,
449 .deserialize_item = scope_deserialize_item,
450
451 .active_state = scope_active_state,
452 .sub_state_to_string = scope_sub_state_to_string,
453
454 .check_gc = scope_check_gc,
455
456 .timer_event = scope_timer_event,
457
458 .notify_cgroup_empty = scope_notify_cgroup_empty_event,
459
460 .bus_interface = "org.freedesktop.systemd1.Scope",
461 .bus_message_handler = bus_scope_message_handler,
462 .bus_set_property = bus_scope_set_property,
463 .bus_commit_properties = bus_scope_commit_properties,
464
465 .can_transient = true
466 };