]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/scope.c
tree-wide: use IN_SET where possible
[thirdparty/systemd.git] / src / core / scope.c
index 1953af1f88a58e1a8e72e4c8c3ab756f68d431e6..4cd5e3dd2a6cb1fdae9d58dcd2dc6d8962de8f55 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
@@ -52,8 +50,7 @@ static void scope_init(Unit *u) {
         assert(u->load_state == UNIT_STUB);
 
         s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
-
-        UNIT(s)->ignore_on_isolate = true;
+        u->ignore_on_isolate = true;
 }
 
 static void scope_done(Unit *u) {
@@ -66,29 +63,27 @@ static void scope_done(Unit *u) {
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 }
 
-static int scope_arm_timer(Scope *s) {
+static int scope_arm_timer(Scope *s, usec_t usec) {
         int r;
 
         assert(s);
 
-        if (s->timeout_stop_usec <= 0) {
-                s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-                return 0;
-        }
-
         if (s->timer_event_source) {
-                r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec);
+                r = sd_event_source_set_time(s->timer_event_source, usec);
                 if (r < 0)
                         return r;
 
                 return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
         }
 
+        if (usec == USEC_INFINITY)
+                return 0;
+
         r = sd_event_add_time(
                         UNIT(s)->manager->event,
                         &s->timer_event_source,
                         CLOCK_MONOTONIC,
-                        now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0,
+                        usec, 0,
                         scope_dispatch_timer, s);
         if (r < 0)
                 return r;
@@ -143,7 +138,7 @@ static int scope_verify(Scope *s) {
                 return 0;
 
         if (set_isempty(UNIT(s)->pids) &&
-            !manager_is_reloading_or_reexecuting(UNIT(s)->manager) &&
+            !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
             !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) {
                 log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
                 return -EINVAL;
@@ -152,6 +147,32 @@ static int scope_verify(Scope *s) {
         return 0;
 }
 
+static int scope_load_init_scope(Unit *u) {
+        assert(u);
+
+        if (!unit_has_name(u, SPECIAL_INIT_SCOPE))
+                return 0;
+
+        u->transient = true;
+        u->perpetual = true;
+
+        /* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we
+         * synthesize it here, instead of relying on the unit file on disk. */
+
+        u->default_dependencies = false;
+        u->ignore_on_isolate = true;
+
+        SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
+
+        /* Prettify things, if we can. */
+        if (!u->description)
+                u->description = strdup("System and Service Manager");
+        if (!u->documentation)
+                (void) strv_extend(&u->documentation, "man:systemd(1)");
+
+        return 1;
+}
+
 static int scope_load(Unit *u) {
         Scope *s = SCOPE(u);
         int r;
@@ -159,26 +180,30 @@ static int scope_load(Unit *u) {
         assert(s);
         assert(u->load_state == UNIT_STUB);
 
-        if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
+        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
+                /* Refuse to load non-transient scope units, but allow them while reloading. */
                 return -ENOENT;
 
-        u->load_state = UNIT_LOADED;
-
-        r = unit_load_dropin(u);
+        r = scope_load_init_scope(u);
         if (r < 0)
                 return r;
-
-        r = unit_patch_contexts(u);
+        r = unit_load_fragment_and_dropin_optional(u);
         if (r < 0)
                 return r;
 
-        r = unit_set_default_slice(u);
-        if (r < 0)
-                return r;
+        if (u->load_state == UNIT_LOADED) {
+                r = unit_patch_contexts(u);
+                if (r < 0)
+                        return r;
 
-        r = scope_add_default_dependencies(s);
-        if (r < 0)
-                return r;
+                r = unit_set_default_slice(u);
+                if (r < 0)
+                        return r;
+
+                r = scope_add_default_dependencies(s);
+                if (r < 0)
+                        return r;
+        }
 
         return scope_verify(s);
 }
@@ -190,20 +215,19 @@ static int scope_coldplug(Unit *u) {
         assert(s);
         assert(s->state == SCOPE_DEAD);
 
-        if (s->deserialized_state != s->state) {
-
-                if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
-                        r = scope_arm_timer(s);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
-                        unit_watch_all_pids(UNIT(s));
+        if (s->deserialized_state == s->state)
+                return 0;
 
-                scope_set_state(s, s->deserialized_state);
+        if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
+                r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec));
+                if (r < 0)
+                        return r;
         }
 
+        if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
+                unit_watch_all_pids(UNIT(s));
+
+        scope_set_state(s, s->deserialized_state);
         return 0;
 }
 
@@ -226,9 +250,12 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) {
 static void scope_enter_dead(Scope *s, ScopeResult f) {
         assert(s);
 
-        if (f != SCOPE_SUCCESS)
+        if (s->result == SCOPE_SUCCESS)
                 s->result = f;
 
+        if (s->result != SCOPE_SUCCESS)
+                log_unit_warning(UNIT(s), "Failed with result '%s'.", scope_result_to_string(s->result));
+
         scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
 }
 
@@ -238,30 +265,33 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
 
         assert(s);
 
-        if (f != SCOPE_SUCCESS)
+        if (s->result == SCOPE_SUCCESS)
                 s->result = f;
 
         unit_watch_all_pids(UNIT(s));
 
         /* If we have a controller set let's ask the controller nicely
          * to terminate the scope, instead of us going directly into
-         * SIGTERM beserk mode */
+         * SIGTERM berserk mode */
         if (state == SCOPE_STOP_SIGTERM)
                 skip_signal = bus_scope_send_request_stop(s) > 0;
 
-        if (!skip_signal) {
+        if (skip_signal)
+                r = 1; /* wait */
+        else {
                 r = unit_kill_context(
                                 UNIT(s),
                                 &s->kill_context,
-                                state != SCOPE_STOP_SIGTERM ? KILL_KILL : KILL_TERMINATE,
+                                state != SCOPE_STOP_SIGTERM ? KILL_KILL :
+                                s->was_abandoned            ? KILL_TERMINATE_AND_LOG :
+                                                              KILL_TERMINATE,
                                 -1, -1, false);
                 if (r < 0)
                         goto fail;
-        } else
-                r = 1;
+        }
 
         if (r > 0) {
-                r = scope_arm_timer(s);
+                r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
                 if (r < 0)
                         goto fail;
 
@@ -292,17 +322,21 @@ static int scope_start(Unit *u) {
                 return -EPERM;
 
         /* We can't fulfill this right now, please try again later */
-        if (s->state == SCOPE_STOP_SIGTERM ||
-            s->state == SCOPE_STOP_SIGKILL)
+        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
                 return -EAGAIN;
 
         assert(s->state == SCOPE_DEAD);
 
-        if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
+        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
                 return -ENOENT;
 
+        r = unit_acquire_invocation_id(u);
+        if (r < 0)
+                return r;
+
         (void) unit_realize_cgroup(u);
-        (void) unit_reset_cpu_usage(u);
+        (void) unit_reset_cpu_accounting(u);
+        (void) unit_reset_ip_accounting(u);
 
         r = unit_attach_pids_to_cgroup(u);
         if (r < 0) {
@@ -322,12 +356,10 @@ static int scope_stop(Unit *u) {
 
         assert(s);
 
-        if (s->state == SCOPE_STOP_SIGTERM ||
-            s->state == SCOPE_STOP_SIGKILL)
+        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
                 return 0;
 
-        assert(s->state == SCOPE_RUNNING ||
-               s->state == SCOPE_ABANDONED);
+        assert(IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED));
 
         scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
         return 1;
@@ -348,17 +380,21 @@ static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
         return unit_kill_common(u, who, signo, -1, -1, error);
 }
 
-static int scope_get_timeout(Unit *u, uint64_t *timeout) {
+static int scope_get_timeout(Unit *u, usec_t *timeout) {
         Scope *s = SCOPE(u);
+        usec_t t;
         int r;
 
         if (!s->timer_event_source)
                 return 0;
 
-        r = sd_event_source_get_time(s->timer_event_source, timeout);
+        r = sd_event_source_get_time(s->timer_event_source, &t);
         if (r < 0)
                 return r;
+        if (t == USEC_INFINITY)
+                return 0;
 
+        *timeout = t;
         return 1;
 }
 
@@ -370,6 +406,7 @@ static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
         assert(fds);
 
         unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
+        unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned));
         return 0;
 }
 
@@ -390,6 +427,14 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
                 else
                         s->deserialized_state = state;
 
+        } else if (streq(key, "was-abandoned")) {
+                int k;
+
+                k = parse_boolean(value);
+                if (k < 0)
+                        log_unit_debug(u, "Failed to parse boolean value: %s", value);
+                else
+                        s->was_abandoned = k;
         } else
                 log_unit_debug(u, "Unknown serialization key: %s", key);
 
@@ -429,8 +474,9 @@ static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         unit_tidy_watch_pids(u, 0, 0);
         unit_watch_all_pids(u);
 
-        /* If the PID set is empty now, then let's finish this off */
-        if (set_isempty(u->pids))
+        /* If the PID set is empty now, then let's finish this off
+           (On unified we use proper notifications) */
+        if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
                 scope_notify_cgroup_empty_event(u);
 }
 
@@ -474,6 +520,7 @@ int scope_abandon(Scope *s) {
         if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
                 return -ESTALE;
 
+        s->was_abandoned = true;
         s->controller = mfree(s->controller);
 
         /* The client is no longer watching the remaining processes,
@@ -518,34 +565,16 @@ static void scope_enumerate(Manager *m) {
 
         u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
         if (!u) {
-                u = unit_new(m, sizeof(Scope));
-                if (!u) {
-                        log_oom();
-                        return;
-                }
-
-                r = unit_add_name(u, SPECIAL_INIT_SCOPE);
+                r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u);
                 if (r < 0)  {
-                        unit_free(u);
-                        log_error_errno(r, "Failed to add init.scope name");
+                        log_error_errno(r, "Failed to allocate the special " SPECIAL_INIT_SCOPE " unit: %m");
                         return;
                 }
         }
 
         u->transient = true;
-        u->default_dependencies = false;
-        u->no_gc = true;
-        u->ignore_on_isolate = true;
-        u->refuse_manual_start = true;
-        u->refuse_manual_stop = true;
+        u->perpetual = true;
         SCOPE(u)->deserialized_state = SCOPE_RUNNING;
-        SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
-
-        /* Prettify things, if we can. */
-        if (!u->description)
-                u->description = strdup("System and Service Manager");
-        if (!u->documentation)
-                (void) strv_extend(&u->documentation, "man:systemd(1)");
 
         unit_add_to_load_queue(u);
         unit_add_to_dbus_queue(u);
@@ -570,8 +599,6 @@ const UnitVTable scope_vtable = {
                 "Install\0",
         .private_section = "Scope",
 
-        .no_alias = true,
-        .no_instances = true,
         .can_transient = true,
 
         .init = scope_init,