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