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