core: rework how we count the n_on_console counter

Let's add a per-unit boolean that tells us whether our unit is currently
counted or not. This way it's unlikely we get out of sync again and
things are generally more robust.

This also allows us to remove the counting logic specific to service
units (which was in fact mostly a copy from the generic implementation),
in favour of fully generic code.

Replaces: #7824
This commit is contained in:
Lennart Poettering 2018-01-24 19:59:55 +01:00
parent bb2c768545
commit adefcf2821
5 changed files with 40 additions and 38 deletions

View File

@ -4072,6 +4072,21 @@ char *manager_taint_string(Manager *m) {
return buf;
}
void manager_ref_console(Manager *m) {
assert(m);
m->n_on_console++;
}
void manager_unref_console(Manager *m) {
assert(m->n_on_console > 0);
m->n_on_console--;
if (m->n_on_console == 0)
m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */
}
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",

View File

@ -452,6 +452,9 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value);
char *manager_taint_string(Manager *m);
void manager_ref_console(Manager *m);
void manager_unref_console(Manager *m);
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;

View File

@ -1060,26 +1060,6 @@ static void service_set_state(Service *s, ServiceState state) {
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
unit_prune_cgroup(UNIT(s));
/* For remain_after_exit services, let's see if we can "release" the
* hold on the console, since unit_notify() only does that in case of
* change of state */
if (state == SERVICE_EXITED &&
s->remain_after_exit &&
UNIT(s)->manager->n_on_console > 0) {
ExecContext *ec;
ec = unit_get_exec_context(UNIT(s));
if (ec && exec_context_may_touch_console(ec)) {
Manager *m = UNIT(s)->manager;
m->n_on_console--;
if (m->n_on_console == 0)
/* unset no_console_output flag, since the console is free */
m->no_console_output = false;
}
}
if (old_state != state)
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));

View File

@ -630,6 +630,9 @@ void unit_free(Unit *u) {
if (u->in_cgroup_empty_queue)
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
if (u->on_console)
manager_unref_console(u->manager);
unit_release_cgroup(u);
if (!MANAGER_IS_RELOADING(u->manager))
@ -2305,6 +2308,23 @@ finish:
}
static void unit_update_on_console(Unit *u) {
bool b;
assert(u);
b = unit_needs_console(u);
if (u->on_console == b)
return;
u->on_console = b;
if (b)
manager_ref_console(u->manager);
else
manager_unref_console(u->manager);
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
Manager *m;
bool unexpected;
@ -2345,24 +2365,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
unit_unlink_state_files(u);
}
/* Note that this doesn't apply to RemainAfterExit services exiting
* successfully, since there's no change of state in that case. Which is
* why it is handled in service_set_state() */
if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ExecContext *ec;
ec = unit_get_exec_context(u);
if (ec && exec_context_may_touch_console(ec)) {
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
m->n_on_console--;
if (m->n_on_console == 0)
/* unset no_console_output flag, since the console is free */
m->no_console_output = false;
} else
m->n_on_console++;
}
}
unit_update_on_console(u);
if (u->job) {
unexpected = false;

View File

@ -340,6 +340,7 @@ struct Unit {
bool sent_dbus_new_signal:1;
bool in_audit:1;
bool on_console:1;
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;