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