]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/scope.c
Configurable Timeouts/Restarts default values
[thirdparty/systemd.git] / src / core / scope.c
CommitLineData
6c12b52e
LP
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
35static 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
43static void scope_init(Unit *u) {
44 Scope *s = SCOPE(u);
45
46 assert(u);
47 assert(u->load_state == UNIT_STUB);
48
1f19a534 49 s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
6c12b52e
LP
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
60static 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
73static 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
93static 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
109static int scope_verify(Scope *s) {
110 assert(s);
111
112 if (UNIT(s)->load_state != UNIT_LOADED)
113 return 0;
114
0c5778a2 115 if (set_size(s->pids) <= 0 && UNIT(s)->manager->n_reloading <= 0) {
6c12b52e
LP
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
123static 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
152static 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
175static 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
191static 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
200static 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
229fail:
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
236static int scope_start(Unit *u) {
237 Scope *s = SCOPE(u);
238 int r;
239
240 assert(s);
241
7b617155
LP
242 if (s->state == SCOPE_FAILED)
243 return -EPERM;
244
6c12b52e
LP
245 if (s->state == SCOPE_STOP_SIGTERM ||
246 s->state == SCOPE_STOP_SIGKILL)
247 return -EAGAIN;
248
249 assert(s->state == SCOPE_DEAD);
250
251 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
252 return -ENOENT;
253
254 r = unit_realize_cgroup(u);
255 if (r < 0) {
256 log_error("Failed to realize cgroup: %s", strerror(-r));
257 return r;
258 }
259
13b84ec7 260 r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, s->pids);
6c12b52e
LP
261 if (r < 0)
262 return r;
263
264 set_free(s->pids);
265 s->pids = NULL;
266
267 s->result = SCOPE_SUCCESS;
268
269 scope_set_state(s, SCOPE_RUNNING);
270 return 0;
271}
272
273static int scope_stop(Unit *u) {
274 Scope *s = SCOPE(u);
275
276 assert(s);
277 assert(s->state == SCOPE_RUNNING);
278
279 if (s->state == SCOPE_STOP_SIGTERM ||
280 s->state == SCOPE_STOP_SIGKILL)
281 return 0;
282
283 assert(s->state == SCOPE_RUNNING);
284
285 scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
286 return 0;
287}
288
8bcca7e2
LP
289static void scope_reset_failed(Unit *u) {
290 Scope *s = SCOPE(u);
291
292 assert(s);
293
294 if (s->state == SCOPE_FAILED)
295 scope_set_state(s, SCOPE_DEAD);
296
297 s->result = SCOPE_SUCCESS;
298}
299
6c12b52e
LP
300static int scope_kill(Unit *u, KillWho who, int signo, DBusError *error) {
301 return unit_kill_common(u, who, signo, -1, -1, error);
302}
303
304static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
305 Scope *s = SCOPE(u);
306
307 assert(s);
308 assert(f);
309 assert(fds);
310
311 unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
312 return 0;
313}
314
315static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
316 Scope *s = SCOPE(u);
317
318 assert(u);
319 assert(key);
320 assert(value);
321 assert(fds);
322
323 if (streq(key, "state")) {
324 ScopeState state;
325
326 state = scope_state_from_string(value);
327 if (state < 0)
328 log_debug("Failed to parse state value %s", value);
329 else
330 s->deserialized_state = state;
331
332 } else
333 log_debug("Unknown serialization key '%s'", key);
334
335 return 0;
336}
337
338static bool scope_check_gc(Unit *u) {
339 Scope *s = SCOPE(u);
340 int r;
341
342 assert(s);
343
344 /* Never clean up scopes that still have a process around,
345 * even if the scope is formally dead. */
346
347 if (UNIT(s)->cgroup_path) {
348 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
349 if (r <= 0)
350 return true;
351 }
352
353 return false;
354}
355
356static void scope_timer_event(Unit *u, uint64_t elapsed, Watch*w) {
357 Scope *s = SCOPE(u);
358
359 assert(s);
360 assert(elapsed == 1);
361 assert(w == &s->timer_watch);
362
363 switch (s->state) {
364
365 case SCOPE_STOP_SIGTERM:
366 if (s->kill_context.send_sigkill) {
367 log_warning_unit(u->id, "%s stopping timed out. Killing.", u->id);
368 scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
369 } else {
370 log_warning_unit(u->id, "%s stopping timed out. Skipping SIGKILL.", u->id);
371 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
372 }
373
374 break;
375
376 case SCOPE_STOP_SIGKILL:
377 log_warning_unit(u->id, "%s still around after SIGKILL. Ignoring.", u->id);
378 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
379 break;
380
381 default:
382 assert_not_reached("Timeout at wrong time.");
383 }
384}
385
386static void scope_notify_cgroup_empty_event(Unit *u) {
387 Scope *s = SCOPE(u);
388 assert(u);
389
390 log_debug_unit(u->id, "%s: cgroup is empty", u->id);
391
392 switch (s->state) {
393
394 case SCOPE_RUNNING:
395 case SCOPE_STOP_SIGTERM:
396 case SCOPE_STOP_SIGKILL:
397 scope_enter_dead(s, SCOPE_SUCCESS);
398
399 break;
400
401 default:
402 ;
403 }
404}
405
406_pure_ static UnitActiveState scope_active_state(Unit *u) {
407 assert(u);
408
409 return state_translation_table[SCOPE(u)->state];
410}
411
412_pure_ static const char *scope_sub_state_to_string(Unit *u) {
413 assert(u);
414
415 return scope_state_to_string(SCOPE(u)->state);
416}
417
418static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
419 [SCOPE_DEAD] = "dead",
358712f3 420 [SCOPE_RUNNING] = "running",
6c12b52e
LP
421 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
422 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
423 [SCOPE_FAILED] = "failed",
424};
425
426DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
427
428static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
429 [SCOPE_SUCCESS] = "success",
430 [SCOPE_FAILURE_RESOURCES] = "resources",
431 [SCOPE_FAILURE_TIMEOUT] = "timeout",
432};
433
434DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
435
436const UnitVTable scope_vtable = {
437 .object_size = sizeof(Scope),
438 .sections =
439 "Unit\0"
440 "Scope\0"
441 "Install\0",
442
443 .private_section = "Scope",
444 .cgroup_context_offset = offsetof(Scope, cgroup_context),
445
446 .no_alias = true,
447 .no_instances = true,
448
449 .init = scope_init,
450 .load = scope_load,
451 .done = scope_done,
452
453 .coldplug = scope_coldplug,
454
455 .dump = scope_dump,
456
457 .start = scope_start,
458 .stop = scope_stop,
459
460 .kill = scope_kill,
461
462 .serialize = scope_serialize,
463 .deserialize_item = scope_deserialize_item,
464
465 .active_state = scope_active_state,
466 .sub_state_to_string = scope_sub_state_to_string,
467
468 .check_gc = scope_check_gc,
469
470 .timer_event = scope_timer_event,
471
8bcca7e2
LP
472 .reset_failed = scope_reset_failed,
473
6c12b52e
LP
474 .notify_cgroup_empty = scope_notify_cgroup_empty_event,
475
476 .bus_interface = "org.freedesktop.systemd1.Scope",
477 .bus_message_handler = bus_scope_message_handler,
478 .bus_set_property = bus_scope_set_property,
479 .bus_commit_properties = bus_scope_commit_properties,
480
481 .can_transient = true
482};