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