chiark / gitweb /
implement recursive_stop/stop_when_unneeded unit flags
[elogind.git] / unit.c
diff --git a/unit.c b/unit.c
index e6e5574312161668f8b7133010cd815bc2267d85..839e542e1e96b34cda55214238e435d09a1bc901 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -44,7 +44,7 @@ UnitType unit_name_to_type(const char *n) {
         "0123456789"                            \
         "abcdefghijklmnopqrstuvwxyz"            \
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
-        "-_"
+        "-_.\\"
 
 bool unit_name_is_valid(const char *n) {
         UnitType t;
@@ -62,6 +62,9 @@ bool unit_name_is_valid(const char *n) {
         if (!(e = strrchr(n, '.')))
                 return false;
 
+        if (e == n)
+                return false;
+
         for (i = n; i < e; i++)
                 if (!strchr(VALID_CHARS, *i))
                         return false;
@@ -144,6 +147,9 @@ int unit_add_name(Unit *u, const char *text) {
                 return r;
         }
 
+        if (u->meta.type == _UNIT_TYPE_INVALID)
+                LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta);
+
         u->meta.type = t;
 
         if (!u->meta.id)
@@ -152,6 +158,34 @@ int unit_add_name(Unit *u, const char *text) {
         return 0;
 }
 
+int unit_choose_id(Unit *u, const char *name) {
+        char *s;
+
+        assert(u);
+        assert(name);
+
+        /* Selects one of the names of this unit as the id */
+
+        if (!(s = set_get(u->meta.names, (char*) name)))
+                return -ENOENT;
+
+        u->meta.id = s;
+        return 0;
+}
+
+int unit_set_description(Unit *u, const char *description) {
+        char *s;
+
+        assert(u);
+
+        if (!(s = strdup(description)))
+                return -ENOMEM;
+
+        free(u->meta.description);
+        u->meta.description = s;
+        return 0;
+}
+
 void unit_add_to_load_queue(Unit *u) {
         assert(u);
 
@@ -193,6 +227,9 @@ void unit_free(Unit *u) {
         SET_FOREACH(t, u->meta.names, i)
                 hashmap_remove_value(u->meta.manager->units, t, u);
 
+        if (u->meta.type != _UNIT_TYPE_INVALID)
+                LIST_REMOVE(Meta, units_per_type, u->meta.manager->units_per_type[u->meta.type], &u->meta);
+
         if (u->meta.in_load_queue)
                 LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta);
 
@@ -240,7 +277,8 @@ static int ensure_merge(Set **s, Set *other) {
         return 0;
 }
 
-/* FIXME: Does not rollback on failure! */
+/* FIXME: Does not rollback on failure! Needs to fix special unit
+ * pointers. Needs to merge names and dependencies properly.*/
 int unit_merge(Unit *u, Unit *other) {
         int r;
         UnitDependency d;
@@ -335,11 +373,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 "%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\tRecursive Deactivate: %s\n"
+                "%s\tStop When Unneeded: %s\n",
                 prefix, unit_id(u),
                 prefix, unit_description(u),
                 prefix, load_state_table[u->meta.load_state],
-                prefix, active_state_table[unit_active_state(u)]);
+                prefix, active_state_table[unit_active_state(u)],
+                prefix, yes_no(u->meta.recursive_stop),
+                prefix, yes_no(u->meta.stop_when_unneeded));
 
         if (u->meta.load_path)
                 fprintf(f, "%s\tLoad Path: %s\n", prefix, u->meta.load_path);
@@ -368,7 +410,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
 
 /* Common implementation for multiple backends */
 int unit_load_fragment_and_dropin(Unit *u) {
-        int r;
+        int r, ret;
 
         assert(u);
 
@@ -376,11 +418,13 @@ int unit_load_fragment_and_dropin(Unit *u) {
         if ((r = unit_load_fragment(u)) < 0)
                 return r;
 
+        ret = r > 0;
+
         /* Load drop-in directory data */
         if ((r = unit_load_dropin(u)) < 0)
                 return r;
 
-        return 0;
+        return ret;
 }
 
 int unit_load(Unit *u) {
@@ -498,6 +542,39 @@ bool unit_can_reload(Unit *u) {
         return UNIT_VTABLE(u)->can_reload(u);
 }
 
+static void unit_check_uneeded(Unit *u) {
+        Iterator i;
+        Unit *other;
+
+        assert(u);
+
+        /* If this service shall be shut down when unneeded then do
+         * so. */
+
+        if (!u->meta.stop_when_unneeded)
+                return;
+
+        if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
+                return;
+
+        SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        return;
+
+        SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        return;
+
+        SET_FOREACH(other, u->meta.dependencies[UNIT_WANTED_BY], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        return;
+
+        log_debug("Service %s is not needed anymore. Stopping.", unit_id(u));
+
+        /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
+        manager_add_job(u->meta.manager, JOB_STOP, u, JOB_FAIL, true, NULL);
+}
+
 static void retroactively_start_dependencies(Unit *u) {
         Iterator i;
         Unit *other;
@@ -533,9 +610,29 @@ static void retroactively_stop_dependencies(Unit *u) {
         assert(u);
         assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
 
-        SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
+        if (u->meta.recursive_stop) {
+                /* Pull down units need us recursively if enabled */
+                SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
+                        if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                                manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL);
+        }
+
+        /* Garbage collect services that might not be needed anymore, if enabled */
+        SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i)
                 if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
-                        manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL);
+                        unit_check_uneeded(other);
+        SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRES], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        unit_check_uneeded(other);
+        SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        unit_check_uneeded(other);
+        SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        unit_check_uneeded(other);
+        SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUISITE], i)
+                if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+                        unit_check_uneeded(other);
 }
 
 void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
@@ -565,7 +662,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                 else {
                         assert(u->meta.job->state == JOB_RUNNING);
 
-                        /* Let's check of this state change
+                        /* Let's check whether this state change
                          * constitutes a finished job, or maybe
                          * cotradicts a running job and hence needs to
                          * invalidate jobs. */
@@ -625,6 +722,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                 retroactively_start_dependencies(u);
         else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
                 retroactively_stop_dependencies(u);
+
+        /* Maybe we finished startup and are now ready for being
+         * stopped because unneeded? */
+        unit_check_uneeded(u);
 }
 
 int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
@@ -724,7 +825,7 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
 
                 zero(ev);
                 ev.data.ptr = w;
-                ev.events = POLLIN;
+                ev.events = EPOLLIN;
 
                 if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
                         goto fail;
@@ -830,6 +931,19 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
         return 0;
 }
 
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name) {
+        Unit *other;
+        int r;
+
+        if ((r = manager_load_unit(u->meta.manager, name, &other)) < 0)
+                return r;
+
+        if ((r = unit_add_dependency(u, d, other)) < 0)
+                return r;
+
+        return 0;
+}
+
 const char *unit_path(void) {
         char *e;
 
@@ -868,3 +982,51 @@ int set_unit_path(const char *p) {
 
         return 0;
 }
+
+char *unit_name_escape_path(const char *prefix, const char *path, const char *suffix) {
+        char *r, *t;
+        const char *f;
+        size_t a, b, c;
+
+        assert(path);
+
+        /* Takes a path and a suffix and prefix and makes a nice
+         * string suitable as unit name of it, escaping all weird
+         * chars on the way.
+         *
+         * / becomes ., and all chars not alloweed in a unit name get
+         * escaped as \xFF, including \ and ., of course. This
+         * escaping is hence reversible.
+         */
+
+        if (!prefix)
+                prefix = "";
+
+        if (!suffix)
+                suffix = "";
+
+        a = strlen(prefix);
+        b = strlen(path);
+        c = strlen(suffix);
+
+        if (!(r = new(char, a+b*4+c+1)))
+                return NULL;
+
+        memcpy(r, prefix, a);
+
+        for (f = path, t = r+a; *f; f++) {
+                if (*f == '/')
+                        *(t++) = '.';
+                else if (*f == '.' || *f == '\\' || !strchr(VALID_CHARS, *f)) {
+                        *(t++) = '\\';
+                        *(t++) = 'x';
+                        *(t++) = hexchar(*f > 4);
+                        *(t++) = hexchar(*f);
+                } else
+                        *(t++) = *f;
+        }
+
+        memcpy(t, suffix, c+1);
+
+        return r;
+}