]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/scope.c
core: rework context initialization/destruction logic
[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_ABANDONED] = UNIT_ACTIVE,
39 [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
40 [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
41 [SCOPE_FAILED] = UNIT_FAILED
42 };
43
44 static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
45
46 static void scope_init(Unit *u) {
47 Scope *s = SCOPE(u);
48
49 assert(u);
50 assert(u->load_state == UNIT_STUB);
51
52 s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
53
54 UNIT(s)->ignore_on_isolate = true;
55 UNIT(s)->ignore_on_snapshot = true;
56 }
57
58 static void scope_done(Unit *u) {
59 Scope *s = SCOPE(u);
60
61 assert(u);
62
63 free(s->controller);
64
65 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
66 }
67
68 static int scope_arm_timer(Scope *s) {
69 int r;
70
71 assert(s);
72
73 if (s->timeout_stop_usec <= 0) {
74 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
75 return 0;
76 }
77
78 if (s->timer_event_source) {
79 r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec);
80 if (r < 0)
81 return r;
82
83 return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
84 }
85
86 return sd_event_add_monotonic(UNIT(s)->manager->event, &s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0, scope_dispatch_timer, s);
87 }
88
89 static void scope_set_state(Scope *s, ScopeState state) {
90 ScopeState old_state;
91 assert(s);
92
93 old_state = s->state;
94 s->state = state;
95
96 if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
97 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
98
99 if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED))
100 unit_unwatch_all_pids(UNIT(s));
101
102 if (state != old_state)
103 log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state));
104
105 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
106 }
107
108 static int scope_add_default_dependencies(Scope *s) {
109 int r;
110
111 assert(s);
112
113 /* Make sure scopes are unloaded on shutdown */
114 r = unit_add_two_dependencies_by_name(
115 UNIT(s),
116 UNIT_BEFORE, UNIT_CONFLICTS,
117 SPECIAL_SHUTDOWN_TARGET, NULL, true);
118 if (r < 0)
119 return r;
120
121 return 0;
122 }
123
124 static int scope_verify(Scope *s) {
125 assert(s);
126
127 if (UNIT(s)->load_state != UNIT_LOADED)
128 return 0;
129
130 if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) {
131 log_error_unit(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id);
132 return -EINVAL;
133 }
134
135 return 0;
136 }
137
138 static int scope_load(Unit *u) {
139 Scope *s = SCOPE(u);
140 int r;
141
142 assert(s);
143 assert(u->load_state == UNIT_STUB);
144
145 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
146 return -ENOENT;
147
148 u->load_state = UNIT_LOADED;
149
150 r = unit_load_dropin(u);
151 if (r < 0)
152 return r;
153
154 r = unit_patch_contexts(u);
155 if (r < 0)
156 return r;
157
158 r = unit_add_default_slice(u, &s->cgroup_context);
159 if (r < 0)
160 return r;
161
162 if (u->default_dependencies) {
163 r = scope_add_default_dependencies(s);
164 if (r < 0)
165 return r;
166 }
167
168 return scope_verify(s);
169 }
170
171 static int scope_coldplug(Unit *u) {
172 Scope *s = SCOPE(u);
173 int r;
174
175 assert(s);
176 assert(s->state == SCOPE_DEAD);
177
178 if (s->deserialized_state != s->state) {
179
180 if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
181 r = scope_arm_timer(s);
182 if (r < 0)
183 return r;
184 }
185
186 if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
187 unit_watch_all_pids(UNIT(s));
188
189 scope_set_state(s, s->deserialized_state);
190 }
191
192 return 0;
193 }
194
195 static void scope_dump(Unit *u, FILE *f, const char *prefix) {
196 Scope *s = SCOPE(u);
197
198 assert(s);
199 assert(f);
200
201 fprintf(f,
202 "%sScope State: %s\n"
203 "%sResult: %s\n",
204 prefix, scope_state_to_string(s->state),
205 prefix, scope_result_to_string(s->result));
206
207 cgroup_context_dump(&s->cgroup_context, f, prefix);
208 kill_context_dump(&s->kill_context, f, prefix);
209 }
210
211 static void scope_enter_dead(Scope *s, ScopeResult f) {
212 assert(s);
213
214 if (f != SCOPE_SUCCESS)
215 s->result = f;
216
217 scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
218 }
219
220 static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
221 bool skip_signal = false;
222 int r;
223
224 assert(s);
225
226 if (f != SCOPE_SUCCESS)
227 s->result = f;
228
229 unit_watch_all_pids(UNIT(s));
230
231 /* If we have a controller set let's ask the controller nicely
232 * to terminate the scope, instead of us going directly into
233 * SIGTERM beserk mode */
234 if (state == SCOPE_STOP_SIGTERM)
235 skip_signal = bus_scope_send_request_stop(s) > 0;
236
237 if (!skip_signal) {
238 r = unit_kill_context(
239 UNIT(s),
240 &s->kill_context,
241 state != SCOPE_STOP_SIGTERM,
242 -1, -1, false);
243 if (r < 0)
244 goto fail;
245 } else
246 r = 1;
247
248 if (r > 0) {
249 r = scope_arm_timer(s);
250 if (r < 0)
251 goto fail;
252
253 scope_set_state(s, state);
254 } else if (state == SCOPE_STOP_SIGTERM)
255 scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS);
256 else
257 scope_enter_dead(s, SCOPE_SUCCESS);
258
259 return;
260
261 fail:
262 log_warning_unit(UNIT(s)->id,
263 "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
264
265 scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
266 }
267
268 static int scope_start(Unit *u) {
269 Scope *s = SCOPE(u);
270 int r;
271
272 assert(s);
273
274 if (s->state == SCOPE_FAILED)
275 return -EPERM;
276
277 if (s->state == SCOPE_STOP_SIGTERM ||
278 s->state == SCOPE_STOP_SIGKILL)
279 return -EAGAIN;
280
281 assert(s->state == SCOPE_DEAD);
282
283 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
284 return -ENOENT;
285
286 r = unit_realize_cgroup(u);
287 if (r < 0) {
288 log_error("Failed to realize cgroup: %s", strerror(-r));
289 return r;
290 }
291
292 r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, UNIT(s)->pids);
293 if (r < 0)
294 return r;
295
296 s->result = SCOPE_SUCCESS;
297
298 scope_set_state(s, SCOPE_RUNNING);
299 return 0;
300 }
301
302 static int scope_stop(Unit *u) {
303 Scope *s = SCOPE(u);
304
305 assert(s);
306
307 if (s->state == SCOPE_STOP_SIGTERM ||
308 s->state == SCOPE_STOP_SIGKILL)
309 return 0;
310
311 assert(s->state == SCOPE_RUNNING ||
312 s->state == SCOPE_ABANDONED);
313
314 scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
315 return 0;
316 }
317
318 static void scope_reset_failed(Unit *u) {
319 Scope *s = SCOPE(u);
320
321 assert(s);
322
323 if (s->state == SCOPE_FAILED)
324 scope_set_state(s, SCOPE_DEAD);
325
326 s->result = SCOPE_SUCCESS;
327 }
328
329 static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
330 return unit_kill_common(u, who, signo, -1, -1, error);
331 }
332
333 static int scope_get_timeout(Unit *u, uint64_t *timeout) {
334 Scope *s = SCOPE(u);
335 int r;
336
337 if (!s->timer_event_source)
338 return 0;
339
340 r = sd_event_source_get_time(s->timer_event_source, timeout);
341 if (r < 0)
342 return r;
343
344 return 1;
345 }
346
347 static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
348 Scope *s = SCOPE(u);
349
350 assert(s);
351 assert(f);
352 assert(fds);
353
354 unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
355 return 0;
356 }
357
358 static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
359 Scope *s = SCOPE(u);
360
361 assert(u);
362 assert(key);
363 assert(value);
364 assert(fds);
365
366 if (streq(key, "state")) {
367 ScopeState state;
368
369 state = scope_state_from_string(value);
370 if (state < 0)
371 log_debug("Failed to parse state value %s", value);
372 else
373 s->deserialized_state = state;
374
375 } else
376 log_debug("Unknown serialization key '%s'", key);
377
378 return 0;
379 }
380
381 static bool scope_check_gc(Unit *u) {
382 Scope *s = SCOPE(u);
383 int r;
384
385 assert(s);
386
387 /* Never clean up scopes that still have a process around,
388 * even if the scope is formally dead. */
389
390 if (u->cgroup_path) {
391 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
392 if (r <= 0)
393 return true;
394 }
395
396 return false;
397 }
398
399 static void scope_notify_cgroup_empty_event(Unit *u) {
400 Scope *s = SCOPE(u);
401 assert(u);
402
403 log_debug_unit(u->id, "%s: cgroup is empty", u->id);
404
405 if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
406 scope_enter_dead(s, SCOPE_SUCCESS);
407 }
408
409 static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
410
411 /* If we get a SIGCHLD event for one of the processes we were
412 interested in, then we look for others to watch, under the
413 assumption that we'll sooner or later get a SIGCHLD for
414 them, as the original process we watched was probably the
415 parent of them, and they are hence now our children. */
416
417 unit_tidy_watch_pids(u, 0, 0);
418 unit_watch_all_pids(u);
419
420 /* If the PID set is empty now, then let's finish this off */
421 if (set_isempty(u->pids))
422 scope_notify_cgroup_empty_event(u);
423 }
424
425 static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
426 Scope *s = SCOPE(userdata);
427
428 assert(s);
429 assert(s->timer_event_source == source);
430
431 switch (s->state) {
432
433 case SCOPE_STOP_SIGTERM:
434 if (s->kill_context.send_sigkill) {
435 log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id);
436 scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
437 } else {
438 log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL.", UNIT(s)->id);
439 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
440 }
441
442 break;
443
444 case SCOPE_STOP_SIGKILL:
445 log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id);
446 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
447 break;
448
449 default:
450 assert_not_reached("Timeout at wrong time.");
451 }
452
453 return 0;
454 }
455
456 int scope_abandon(Scope *s) {
457 assert(s);
458
459 if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
460 return -ESTALE;
461
462 free(s->controller);
463 s->controller = NULL;
464
465 /* The client is no longer watching the remaining processes,
466 * so let's step in here, under the assumption that the
467 * remaining processes will be sooner or later reassigned to
468 * us as parent. */
469
470 unit_tidy_watch_pids(UNIT(s), 0, 0);
471 unit_watch_all_pids(UNIT(s));
472
473 /* If the PID set is empty now, then let's finish this off */
474 if (set_isempty(UNIT(s)->pids))
475 scope_notify_cgroup_empty_event(UNIT(s));
476 else
477 scope_set_state(s, SCOPE_ABANDONED);
478
479 return 0;
480 }
481
482 _pure_ static UnitActiveState scope_active_state(Unit *u) {
483 assert(u);
484
485 return state_translation_table[SCOPE(u)->state];
486 }
487
488 _pure_ static const char *scope_sub_state_to_string(Unit *u) {
489 assert(u);
490
491 return scope_state_to_string(SCOPE(u)->state);
492 }
493
494 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
495 [SCOPE_DEAD] = "dead",
496 [SCOPE_RUNNING] = "running",
497 [SCOPE_ABANDONED] = "abandoned",
498 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
499 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
500 [SCOPE_FAILED] = "failed",
501 };
502
503 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
504
505 static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
506 [SCOPE_SUCCESS] = "success",
507 [SCOPE_FAILURE_RESOURCES] = "resources",
508 [SCOPE_FAILURE_TIMEOUT] = "timeout",
509 };
510
511 DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
512
513 const UnitVTable scope_vtable = {
514 .object_size = sizeof(Scope),
515 .cgroup_context_offset = offsetof(Scope, cgroup_context),
516 .kill_context_offset = offsetof(Scope, kill_context),
517
518 .sections =
519 "Unit\0"
520 "Scope\0"
521 "Install\0",
522 .private_section = "Scope",
523
524 .no_alias = true,
525 .no_instances = true,
526
527 .init = scope_init,
528 .load = scope_load,
529 .done = scope_done,
530
531 .coldplug = scope_coldplug,
532
533 .dump = scope_dump,
534
535 .start = scope_start,
536 .stop = scope_stop,
537
538 .kill = scope_kill,
539
540 .get_timeout = scope_get_timeout,
541
542 .serialize = scope_serialize,
543 .deserialize_item = scope_deserialize_item,
544
545 .active_state = scope_active_state,
546 .sub_state_to_string = scope_sub_state_to_string,
547
548 .check_gc = scope_check_gc,
549
550 .sigchld_event = scope_sigchld_event,
551
552 .reset_failed = scope_reset_failed,
553
554 .notify_cgroup_empty = scope_notify_cgroup_empty_event,
555
556 .bus_interface = "org.freedesktop.systemd1.Scope",
557 .bus_vtable = bus_scope_vtable,
558 .bus_set_property = bus_scope_set_property,
559 .bus_commit_properties = bus_scope_commit_properties,
560
561 .can_transient = true
562 };