chiark / gitweb /
unit: free data from merged unit when we don't need it anymore
[elogind.git] / unit.c
diff --git a/unit.c b/unit.c
index 10efd14c6a39db11b9d99f6056c24a9f217d882d..d4f0538b36edc901a1b86336d4e226b6a515a685 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -131,6 +131,13 @@ Unit *unit_new(Manager *m) {
         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;
@@ -212,6 +219,7 @@ int unit_set_description(Unit *u, const char *description) {
 
 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;
@@ -232,6 +240,7 @@ void unit_add_to_cleanup_queue(Unit *u) {
 
 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;
@@ -387,6 +396,8 @@ int unit_merge(Unit *u, Unit *other) {
         assert(other);
         assert(u->meta.manager == other->meta.manager);
 
+        other = unit_follow_merge(other);
+
         if (other == u)
                 return 0;
 
@@ -396,7 +407,14 @@ int unit_merge(Unit *u, Unit *other) {
         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 */
@@ -406,11 +424,16 @@ int unit_merge(Unit *u, Unit *other) {
         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;
@@ -819,6 +842,8 @@ static void retroactively_stop_dependencies(Unit *u) {
 }
 
 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);
@@ -855,26 +880,24 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                         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;
 
@@ -882,13 +905,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                         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;
 
@@ -901,10 +923,37 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
         /* 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? */
@@ -1410,3 +1459,11 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
 };
 
 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);