ff2b41
From 1ac6c613ef11a9ee9c36b25133cc302c4be1a858 Mon Sep 17 00:00:00 2001
ff2b41
From: Lennart Poettering <lennart@poettering.net>
ff2b41
Date: Wed, 24 Jan 2018 19:59:55 +0100
ff2b41
Subject: [PATCH] core: rework how we count the n_on_console counter
ff2b41
ff2b41
Let's add a per-unit boolean that tells us whether our unit is currently
ff2b41
counted or not. This way it's unlikely we get out of sync again and
ff2b41
things are generally more robust.
ff2b41
ff2b41
This also allows us to remove the counting logic specific to service
ff2b41
units (which was in fact mostly a copy from the generic implementation),
ff2b41
in favour of fully generic code.
ff2b41
ff2b41
Replaces: #7824
ff2b41
(cherry picked from commit adefcf2821a386184991054ed2bb6dacc90d7419)
ff2b41
ff2b41
Resolves: #1524359
ff2b41
---
ff2b41
 src/core/manager.c | 15 +++++++++++++++
ff2b41
 src/core/manager.h |  3 +++
ff2b41
 src/core/service.c | 20 --------------------
ff2b41
 src/core/unit.c    | 39 +++++++++++++++++++++------------------
ff2b41
 src/core/unit.h    |  1 +
ff2b41
 5 files changed, 40 insertions(+), 38 deletions(-)
ff2b41
ff2b41
diff --git a/src/core/manager.c b/src/core/manager.c
ff2b41
index 88d156e8fb..4c87ad8a2f 100644
ff2b41
--- a/src/core/manager.c
ff2b41
+++ b/src/core/manager.c
ff2b41
@@ -3379,6 +3379,21 @@ ManagerState manager_state(Manager *m) {
ff2b41
         return MANAGER_RUNNING;
ff2b41
 }
ff2b41
 
ff2b41
+void manager_ref_console(Manager *m) {
ff2b41
+        assert(m);
ff2b41
+
ff2b41
+        m->n_on_console++;
ff2b41
+}
ff2b41
+
ff2b41
+void manager_unref_console(Manager *m) {
ff2b41
+
ff2b41
+        assert(m->n_on_console > 0);
ff2b41
+        m->n_on_console--;
ff2b41
+
ff2b41
+        if (m->n_on_console == 0)
ff2b41
+                m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */
ff2b41
+}
ff2b41
+
ff2b41
 static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
ff2b41
         [MANAGER_INITIALIZING] = "initializing",
ff2b41
         [MANAGER_STARTING] = "starting",
ff2b41
diff --git a/src/core/manager.h b/src/core/manager.h
ff2b41
index b0e4cad1fc..cfc564dfb6 100644
ff2b41
--- a/src/core/manager.h
ff2b41
+++ b/src/core/manager.h
ff2b41
@@ -379,5 +379,8 @@ const char *manager_get_runtime_prefix(Manager *m);
ff2b41
 
ff2b41
 ManagerState manager_state(Manager *m);
ff2b41
 
ff2b41
+void manager_ref_console(Manager *m);
ff2b41
+void manager_unref_console(Manager *m);
ff2b41
+
ff2b41
 const char *manager_state_to_string(ManagerState m) _const_;
ff2b41
 ManagerState manager_state_from_string(const char *s) _pure_;
ff2b41
diff --git a/src/core/service.c b/src/core/service.c
ff2b41
index 8a8f4be149..ea71c9e237 100644
ff2b41
--- a/src/core/service.c
ff2b41
+++ b/src/core/service.c
ff2b41
@@ -914,26 +914,6 @@ static void service_set_state(Service *s, ServiceState state) {
ff2b41
         if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
ff2b41
                 unit_destroy_cgroup_if_empty(UNIT(s));
ff2b41
 
ff2b41
-        /* For remain_after_exit services, let's see if we can "release" the
ff2b41
-         * hold on the console, since unit_notify() only does that in case of
ff2b41
-         * change of state */
ff2b41
-        if (state == SERVICE_EXITED &&
ff2b41
-            s->remain_after_exit &&
ff2b41
-            UNIT(s)->manager->n_on_console > 0) {
ff2b41
-
ff2b41
-                ExecContext *ec;
ff2b41
-
ff2b41
-                ec = unit_get_exec_context(UNIT(s));
ff2b41
-                if (ec && exec_context_may_touch_console(ec)) {
ff2b41
-                        Manager *m = UNIT(s)->manager;
ff2b41
-
ff2b41
-                        m->n_on_console --;
ff2b41
-                        if (m->n_on_console == 0)
ff2b41
-                                /* unset no_console_output flag, since the console is free */
ff2b41
-                                m->no_console_output = false;
ff2b41
-                }
ff2b41
-        }
ff2b41
-
ff2b41
         if (old_state != state)
ff2b41
                 log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
ff2b41
 
ff2b41
diff --git a/src/core/unit.c b/src/core/unit.c
ff2b41
index 48358bc026..294c9eb70f 100644
ff2b41
--- a/src/core/unit.c
ff2b41
+++ b/src/core/unit.c
ff2b41
@@ -544,6 +544,9 @@ void unit_free(Unit *u) {
ff2b41
                 u->manager->n_in_gc_queue--;
ff2b41
         }
ff2b41
 
ff2b41
+        if (u->on_console)
ff2b41
+                manager_unref_console(u->manager);
ff2b41
+
ff2b41
         condition_free_list(u->conditions);
ff2b41
         condition_free_list(u->asserts);
ff2b41
 
ff2b41
@@ -1741,6 +1744,23 @@ void unit_trigger_notify(Unit *u) {
ff2b41
                         UNIT_VTABLE(other)->trigger_notify(other, u);
ff2b41
 }
ff2b41
 
ff2b41
+static void unit_update_on_console(Unit *u) {
ff2b41
+        bool b;
ff2b41
+
ff2b41
+        assert(u);
ff2b41
+
ff2b41
+        b = unit_needs_console(u);
ff2b41
+        if (u->on_console == b)
ff2b41
+                return;
ff2b41
+
ff2b41
+        u->on_console = b;
ff2b41
+        if (b)
ff2b41
+                manager_ref_console(u->manager);
ff2b41
+        else
ff2b41
+                manager_unref_console(u->manager);
ff2b41
+
ff2b41
+}
ff2b41
+
ff2b41
 void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
ff2b41
         Manager *m;
ff2b41
         bool unexpected;
ff2b41
@@ -1784,24 +1804,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
ff2b41
         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
ff2b41
                 unit_destroy_cgroup_if_empty(u);
ff2b41
 
ff2b41
-        /* Note that this doesn't apply to RemainAfterExit services exiting
ff2b41
-         * successfully, since there's no change of state in that case. Which is
ff2b41
-         * why it is handled in service_set_state() */
ff2b41
-        if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ff2b41
-                ExecContext *ec;
ff2b41
-
ff2b41
-                ec = unit_get_exec_context(u);
ff2b41
-                if (ec && exec_context_may_touch_console(ec)) {
ff2b41
-                        if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ff2b41
-                                m->n_on_console --;
ff2b41
-
ff2b41
-                                if (m->n_on_console == 0)
ff2b41
-                                        /* unset no_console_output flag, since the console is free */
ff2b41
-                                        m->no_console_output = false;
ff2b41
-                        } else
ff2b41
-                                m->n_on_console ++;
ff2b41
-                }
ff2b41
-        }
ff2b41
+        unit_update_on_console(u);
ff2b41
 
ff2b41
         if (u->job) {
ff2b41
                 unexpected = false;
ff2b41
diff --git a/src/core/unit.h b/src/core/unit.h
ff2b41
index fa7de11645..719fc95260 100644
ff2b41
--- a/src/core/unit.h
ff2b41
+++ b/src/core/unit.h
ff2b41
@@ -238,6 +238,7 @@ struct Unit {
ff2b41
         bool no_gc:1;
ff2b41
 
ff2b41
         bool in_audit:1;
ff2b41
+        bool on_console:1;
ff2b41
 
ff2b41
         bool cgroup_realized:1;
ff2b41
         bool cgroup_members_mask_valid:1;