chiark / gitweb /
systemctl: load unit if necessary
[elogind.git] / unit.c
diff --git a/unit.c b/unit.c
index 7f147537e30e418da1c2dc50e4fc1248f2d1af51..b69b6e36201a6cb218bff5c3d51e95c68293cf28 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -37,6 +37,7 @@
 #include "log.h"
 #include "unit-name.h"
 #include "specifier.h"
+#include "dbus-unit.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -124,6 +125,11 @@ int unit_add_name(Unit *u, const char *text) {
                 goto fail;
         }
 
+        if (hashmap_size(u->meta.manager->units) >= MANAGER_MAX_NAMES) {
+                r = -E2BIG;
+                goto fail;
+        }
+
         if ((r = set_put(u->meta.names, s)) < 0) {
                 if (r == -EEXIST)
                         r = 0;
@@ -203,6 +209,25 @@ int unit_set_description(Unit *u, const char *description) {
         return 0;
 }
 
+bool unit_check_gc(Unit *u) {
+        assert(u);
+
+        if (UNIT_VTABLE(u)->no_gc)
+                return true;
+
+        if (u->meta.job)
+                return true;
+
+        if (unit_active_state(u) != UNIT_INACTIVE)
+                return true;
+
+        if (UNIT_VTABLE(u)->check_gc)
+                if (UNIT_VTABLE(u)->check_gc(u))
+                        return true;
+
+        return false;
+}
+
 void unit_add_to_load_queue(Unit *u) {
         assert(u);
         assert(u->meta.type != _UNIT_TYPE_INVALID);
@@ -224,6 +249,24 @@ void unit_add_to_cleanup_queue(Unit *u) {
         u->meta.in_cleanup_queue = true;
 }
 
+void unit_add_to_gc_queue(Unit *u) {
+        assert(u);
+
+        if (u->meta.in_gc_queue || u->meta.in_cleanup_queue)
+                return;
+
+        if (unit_check_gc(u))
+                return;
+
+        LIST_PREPEND(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta);
+        u->meta.in_gc_queue = true;
+
+        u->meta.manager->n_in_gc_queue ++;
+
+        if (u->meta.manager->gc_queue_timestamp <= 0)
+                u->meta.manager->gc_queue_timestamp = now(CLOCK_MONOTONIC);
+}
+
 void unit_add_to_dbus_queue(Unit *u) {
         assert(u);
         assert(u->meta.type != _UNIT_TYPE_INVALID);
@@ -249,6 +292,8 @@ static void bidi_set_free(Unit *u, Set *s) {
 
                 for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                         set_remove(other->meta.dependencies[d], u);
+
+                unit_add_to_gc_queue(other);
         }
 
         set_free(s);
@@ -279,6 +324,11 @@ void unit_free(Unit *u) {
         if (u->meta.in_cleanup_queue)
                 LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
 
+        if (u->meta.in_gc_queue) {
+                LIST_REMOVE(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta);
+                u->meta.manager->n_in_gc_queue--;
+        }
+
         /* Free data and next 'smaller' objects */
         if (u->meta.job)
                 job_free(u->meta.job);
@@ -481,11 +531,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
         /* If syslog or kernel logging is requested, make sure our own
          * logging daemon is run first. */
 
-        if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL)) < 0)
+        if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0)
                 return r;
 
         if (u->meta.manager->running_as != MANAGER_SESSION)
-                if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL)) < 0)
+                if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0)
                         return r;
 
         return 0;
@@ -518,20 +568,22 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         prefix2 = p2 ? p2 : prefix;
 
         fprintf(f,
-                "%s Unit %s:\n"
+                "%s-> Unit %s:\n"
                 "%s\tDescription: %s\n"
                 "%s\tInstance: %s\n"
                 "%s\tUnit Load State: %s\n"
                 "%s\tUnit Active State: %s\n"
                 "%s\tActive Enter Timestamp: %s\n"
-                "%s\tActive Exit Timestamp: %s\n",
+                "%s\tActive Exit Timestamp: %s\n"
+                "%s\tGC Check Good: %s\n",
                 prefix, u->meta.id,
                 prefix, unit_description(u),
                 prefix, strna(u->meta.instance),
                 prefix, unit_load_state_to_string(u->meta.load_state),
                 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)));
+                prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_exit_timestamp)),
+                prefix, yes_no(unit_check_gc(u)));
 
         SET_FOREACH(t, u->meta.names, i)
                 fprintf(f, "%s\tName: %s\n", prefix, t);
@@ -614,6 +666,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
         return 0;
 }
 
+/* Common implementation for multiple backends */
+int unit_load_nop(Unit *u) {
+        assert(u);
+
+        if (u->meta.load_state == UNIT_STUB)
+                u->meta.load_state = UNIT_LOADED;
+
+        return 0;
+}
+
 int unit_load(Unit *u) {
         int r;
 
@@ -642,6 +704,7 @@ int unit_load(Unit *u) {
         assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
 
         unit_add_to_dbus_queue(unit_follow_merge(u));
+        unit_add_to_gc_queue(u);
 
         return 0;
 
@@ -976,6 +1039,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
         unit_check_uneeded(u);
 
         unit_add_to_dbus_queue(u);
+        unit_add_to_gc_queue(u);
 }
 
 int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
@@ -1023,6 +1087,9 @@ int unit_watch_pid(Unit *u, pid_t pid) {
         assert(u);
         assert(pid >= 1);
 
+        /* Watch a specific PID. We only support one unit watching
+         * each PID for now. */
+
         return hashmap_put(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u);
 }
 
@@ -1030,7 +1097,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
         assert(u);
         assert(pid >= 1);
 
-        hashmap_remove(u->meta.manager->watch_pids, UINT32_TO_PTR(pid));
+        hashmap_remove_value(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u);
 }
 
 int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
@@ -1105,7 +1172,7 @@ void unit_unwatch_timer(Unit *u, Watch *w) {
         assert(w->type == WATCH_TIMER && w->data.unit == u);
 
         assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
-        assert_se(close_nointr(w->fd) == 0);
+        close_nointr_nofail(w->fd);
 
         w->fd = -1;
         w->type = WATCH_INVALID;
@@ -1138,7 +1205,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
         }
 }
 
-int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
+int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
 
         static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
                 [UNIT_REQUIRES] = UNIT_REQUIRED_BY,
@@ -1151,9 +1218,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
                 [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
                 [UNIT_CONFLICTS] = UNIT_CONFLICTS,
                 [UNIT_BEFORE] = UNIT_AFTER,
-                [UNIT_AFTER] = UNIT_BEFORE
+                [UNIT_AFTER] = UNIT_BEFORE,
+                [UNIT_REFERENCES] = UNIT_REFERENCED_BY,
+                [UNIT_REFERENCED_BY] = UNIT_REFERENCES
         };
-        int r;
+        int r, q = 0, v = 0, w = 0;
 
         assert(u);
         assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
@@ -1173,22 +1242,47 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
                     return -EINVAL;
         }
 
-        if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0)
+        if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0 ||
+            (r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
                 return r;
 
-        if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
-                return r;
+        if (add_reference)
+                if ((r = set_ensure_allocated(&u->meta.dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 ||
+                    (r = set_ensure_allocated(&other->meta.dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0)
+                        return r;
 
-        if ((r = set_put(u->meta.dependencies[d], other)) < 0)
-                return r;
+        if ((q = set_put(u->meta.dependencies[d], other)) < 0)
+                return q;
 
-        if ((r = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) {
-                set_remove(u->meta.dependencies[d], other);
-                return r;
+        if ((v = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) {
+                r = v;
+                goto fail;
+        }
+
+        if (add_reference) {
+                if ((w = set_put(u->meta.dependencies[UNIT_REFERENCES], other)) < 0) {
+                        r = w;
+                        goto fail;
+                }
+
+                if ((r = set_put(other->meta.dependencies[UNIT_REFERENCED_BY], u)) < 0)
+                        goto fail;
         }
 
         unit_add_to_dbus_queue(u);
         return 0;
+
+fail:
+        if (q > 0)
+                set_remove(u->meta.dependencies[d], other);
+
+        if (v > 0)
+                set_remove(other->meta.dependencies[inverse_table[d]], u);
+
+        if (w > 0)
+                set_remove(u->meta.dependencies[UNIT_REFERENCES], other);
+
+        return r;
 }
 
 static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
@@ -1224,7 +1318,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
         return s;
 }
 
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path) {
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
         Unit *other;
         int r;
         char *s;
@@ -1238,14 +1332,14 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con
         if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0)
                 goto finish;
 
-        r = unit_add_dependency(u, d, other);
+        r = unit_add_dependency(u, d, other, add_reference);
 
 finish:
         free(s);
         return r;
 }
 
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path) {
+int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
         Unit *other;
         int r;
         char *s;
@@ -1259,7 +1353,7 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n
         if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0)
                 goto finish;
 
-        r = unit_add_dependency(other, d, u);
+        r = unit_add_dependency(other, d, u, add_reference);
 
 finish:
         free(s);
@@ -1339,13 +1433,22 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
 
 static char *default_cgroup_path(Unit *u) {
         char *p;
+        int r;
 
         assert(u);
 
-        if (asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id) < 0)
-                return NULL;
+        if (u->meta.instance) {
+                char *t;
 
-        return p;
+                if (!(t = unit_name_template(u->meta.id)))
+                        return NULL;
+
+                r = asprintf(&p, "%s/%s/%s", u->meta.manager->cgroup_hierarchy, t, u->meta.instance);
+                free(t);
+        } else
+                r = asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id);
+
+        return r < 0 ? NULL : p;
 }
 
 int unit_add_cgroup_from_text(Unit *u, const char *name) {
@@ -1482,6 +1585,29 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
         return r;
 }
 
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+        Unit *found;
+        char *t;
+
+        assert(u);
+        assert(type);
+        assert(_found);
+
+        if (!(t = unit_name_change_suffix(u->meta.id, type)))
+                return -ENOMEM;
+
+        assert(!unit_has_name(u, t));
+
+        found = manager_get_unit(u->meta.manager, t);
+        free(t);
+
+        if (!found)
+                return -ENOENT;
+
+        *_found = found;
+        return 0;
+}
+
 static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
         Unit *u = userdata;
         assert(u);
@@ -1597,6 +1723,114 @@ fail:
         return NULL;
 }
 
+int unit_watch_bus_name(Unit *u, const char *name) {
+        assert(u);
+        assert(name);
+
+        /* Watch a specific name on the bus. We only support one unit
+         * watching each name for now. */
+
+        return hashmap_put(u->meta.manager->watch_bus, name, u);
+}
+
+void unit_unwatch_bus_name(Unit *u, const char *name) {
+        assert(u);
+        assert(name);
+
+        hashmap_remove_value(u->meta.manager->watch_bus, name, u);
+}
+
+bool unit_can_serialize(Unit *u) {
+        assert(u);
+
+        return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
+}
+
+int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
+        int r;
+
+        assert(u);
+        assert(f);
+        assert(fds);
+
+        if (!unit_can_serialize(u))
+                return 0;
+
+        if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
+                return r;
+
+        /* End marker */
+        fputc('\n', f);
+        return 0;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+        va_list ap;
+
+        assert(u);
+        assert(f);
+        assert(key);
+        assert(format);
+
+        fputs(key, f);
+        fputc('=', f);
+
+        va_start(ap, format);
+        vfprintf(f, format, ap);
+        va_end(ap);
+
+        fputc('\n', f);
+}
+
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+        assert(u);
+        assert(f);
+        assert(key);
+        assert(value);
+
+        fprintf(f, "%s=%s\n", key, value);
+}
+
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+        int r;
+
+        assert(u);
+        assert(f);
+        assert(fds);
+
+        if (!unit_can_serialize(u))
+                return 0;
+
+        for (;;) {
+                char line[1024], *l, *v;
+                size_t k;
+
+                if (!fgets(line, sizeof(line), f)) {
+                        if (feof(f))
+                                return 0;
+                        return -errno;
+                }
+
+                l = strstrip(line);
+
+                /* End marker */
+                if (l[0] == 0)
+                        return 0;
+
+                k = strcspn(l, "=");
+
+                if (l[k] == '=') {
+                        l[k] = 0;
+                        v = l+k+1;
+                } else
+                        v = l+k;
+
+                if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+                        return r;
+        }
+}
+
+
 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = "service",
         [UNIT_TIMER] = "timer",
@@ -1640,6 +1874,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
         [UNIT_CONFLICTS] = "Conflicts",
         [UNIT_BEFORE] = "Before",
         [UNIT_AFTER] = "After",
+        [UNIT_REFERENCES] = "References",
+        [UNIT_REFERENCED_BY] = "ReferencedBy"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);