return u;
}
+bool unit_has_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ return !!set_get(u->meta.names, (char*) name);
+}
+
int unit_add_name(Unit *u, const char *text) {
UnitType t;
char *s;
void unit_add_to_load_queue(Unit *u) {
assert(u);
+ assert(u->meta.type != _UNIT_TYPE_INVALID);
if (u->meta.load_state != UNIT_STUB || u->meta.in_load_queue)
return;
void unit_add_to_dbus_queue(Unit *u) {
assert(u);
+ assert(u->meta.type != _UNIT_TYPE_INVALID);
if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue || set_isempty(u->meta.manager->subscribed))
return;
assert(other);
assert(u->meta.manager == other->meta.manager);
+ other = unit_follow_merge(other);
+
if (other == u)
return 0;
if (u->meta.type != u->meta.type)
return -EINVAL;
- if (other->meta.load_state != UNIT_STUB)
+ if (other->meta.load_state != UNIT_STUB &&
+ other->meta.load_state != UNIT_FAILED)
+ return -EEXIST;
+
+ if (other->meta.job)
+ return -EEXIST;
+
+ if (unit_active_state(other) != UNIT_INACTIVE)
return -EEXIST;
/* Merge names */
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
merge_dependencies(u, other, d);
- unit_add_to_dbus_queue(u);
-
other->meta.load_state = UNIT_MERGED;
other->meta.merged_into = u;
+ /* If there is still some data attached to the other node, we
+ * don't need it anymore, and can free it. */
+ if (other->meta.load_state != UNIT_STUB)
+ if (UNIT_VTABLE(other)->done)
+ UNIT_VTABLE(other)->done(other);
+
+ unit_add_to_dbus_queue(u);
unit_add_to_cleanup_queue(other);
return 0;
char *p2;
const char *prefix2;
CGroupBonding *b;
+ char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
assert(u);
"%s→ Unit %s:\n"
"%s\tDescription: %s\n"
"%s\tUnit Load State: %s\n"
- "%s\tUnit Active State: %s\n",
+ "%s\tUnit Active State: %s\n"
+ "%s\tActive Enter Timestamp: %s\n"
+ "%s\tActive Exit Timestamp: %s\n",
prefix, unit_id(u),
prefix, unit_description(u),
prefix, unit_load_state_to_string(u->meta.load_state),
- prefix, unit_active_state_to_string(unit_active_state(u)));
+ prefix, unit_active_state_to_string(unit_active_state(u)),
+ prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.active_enter_timestamp)),
+ prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_exit_timestamp)));
SET_FOREACH(t, u->meta.names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
Unit *other;
- if (set_isempty(u->meta.dependencies[d]))
- continue;
-
SET_FOREACH(other, u->meta.dependencies[d], i)
fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other));
}
- fprintf(f,
- "%s\tRecursive Stop: %s\n"
- "%s\tStop When Unneeded: %s\n",
- prefix, yes_no(u->meta.recursive_stop),
- prefix, yes_no(u->meta.stop_when_unneeded));
-
if (u->meta.load_state == UNIT_LOADED) {
+ fprintf(f,
+ "%s\tRecursive Stop: %s\n"
+ "%s\tStop When Unneeded: %s\n",
+ prefix, yes_no(u->meta.recursive_stop),
+ prefix, yes_no(u->meta.stop_when_unneeded));
+
LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
fprintf(f, "%s\tControlGroup: %s:%s\n",
prefix, b->controller, b->path);
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
- }
+
+ } else if (u->meta.load_state == UNIT_MERGED)
+ fprintf(f,
+ "%s\tMerged into: %s\n",
+ prefix, unit_id(u->meta.merged_into));
if (u->meta.job)
job_dump(u->meta.job, f, prefix2);
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
+ bool unexpected = false;
+
assert(u);
assert(os < _UNIT_ACTIVE_STATE_MAX);
assert(ns < _UNIT_ACTIVE_STATE_MAX);
assert(!(os == UNIT_ACTIVE && ns == UNIT_ACTIVATING));
assert(!(os == UNIT_INACTIVE && ns == UNIT_DEACTIVATING));
- if (os == ns)
- return;
+ /* Note that this is called for all low-level state changes,
+ * even if they might map to the same high-level
+ * UnitActiveState! That means that ns == os is OK an expected
+ * behaviour here. For example: if a mount point is remounted
+ * this function will be called too and the utmp code below
+ * relies on that! */
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
u->meta.active_enter_timestamp = now(CLOCK_REALTIME);
case JOB_START:
case JOB_VERIFY_ACTIVE:
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
job_finish_and_invalidate(u->meta.job, true);
- return;
- } else if (ns == UNIT_ACTIVATING)
- return;
- else
+ else if (ns != UNIT_ACTIVATING) {
+ unexpected = true;
job_finish_and_invalidate(u->meta.job, false);
+ }
break;
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
- if (ns == UNIT_ACTIVE) {
+ if (ns == UNIT_ACTIVE)
job_finish_and_invalidate(u->meta.job, true);
- return;
- } else if (ns == UNIT_ACTIVATING || ns == UNIT_ACTIVE_RELOADING)
- return;
- else
+ else if (ns != UNIT_ACTIVATING && ns != UNIT_ACTIVE_RELOADING) {
+ unexpected = true;
job_finish_and_invalidate(u->meta.job, false);
+ }
break;
case JOB_RESTART:
case JOB_TRY_RESTART:
- if (ns == UNIT_INACTIVE) {
+ if (ns == UNIT_INACTIVE)
job_finish_and_invalidate(u->meta.job, true);
- return;
- } else if (ns == UNIT_DEACTIVATING)
- return;
- else
+ else if (ns != UNIT_DEACTIVATING) {
+ unexpected = true;
job_finish_and_invalidate(u->meta.job, false);
+ }
break;
/* If this state change happened without being requested by a
* job, then let's retroactively start or stop dependencies */
- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
- retroactively_start_dependencies(u);
- else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
- retroactively_stop_dependencies(u);
+ if (unexpected) {
+ if (UNIT_IS_INACTIVE_OR_DEACTIVATING(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
+ retroactively_start_dependencies(u);
+ else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
+ retroactively_stop_dependencies(u);
+ }
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
+
+ if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
+ log_info("D-Bus became available, trying to reconnect.");
+ /* The bus just got started, hence try to connect to it. */
+ bus_init_system(u->meta.manager);
+ bus_init_api(u->meta.manager);
+ }
+
+ if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) {
+ /* The syslog daemon just got started, hence try to connect to it. */
+ log_info("Syslog became available, trying to reconnect.");
+ log_open_syslog();
+ }
+
+ } else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
+
+ if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
+ /* The syslog daemon just got terminated, hence try to disconnect from it. */
+ log_close_syslog();
+
+ /* We don't care about D-Bus here, since we'll get an
+ * asynchronous notification for it anyway. */
+ }
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
};
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
+
+static const char* const kill_mode_table[_KILL_MODE_MAX] = {
+ [KILL_PROCESS] = "process",
+ [KILL_PROCESS_GROUP] = "process-group",
+ [KILL_CONTROL_GROUP] = "control-group"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);