chiark / gitweb /
implement drop-in directories
authorLennart Poettering <lennart@poettering.net>
Tue, 26 Jan 2010 23:15:56 +0000 (00:15 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 26 Jan 2010 23:15:56 +0000 (00:15 +0100)
17 files changed:
Makefile
load-dropin.c
load-fragment.c
main.c
manager.c
manager.h
target.c
test-engine.c
test1/default.target [changed from file to symlink]
test1/mail-transfer-agent.service [new symlink]
test1/mail-transfer-agent.socket [new symlink]
test1/multiuser.target [new file with mode: 0644]
test1/multiuser.target.wants/mail-transfer-agent.socket [new symlink]
unit.c
unit.h
util.c
util.h

index 9a86220..119d3dc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-unused-parameter
+CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-unused-parameter -DUNIT_PATH=\"/tmp/does/not/exist\"
 LIBS=-lrt -lcap
 
 COMMON= \
index 3023cda..b975d83 100644 (file)
@@ -1,11 +1,72 @@
 /*-*- Mode: C; c-basic-offset: 8 -*-*/
 
+#include <dirent.h>
+#include <errno.h>
+
+#include "unit.h"
 #include "load-dropin.h"
 
 int unit_load_dropin(Unit *u) {
+        Iterator i;
+        int r;
+        char *t;
+
         assert(u);
 
         /* Load dependencies from supplementary drop-in directories */
 
+        SET_FOREACH(t, u->meta.names, i) {
+                char *path;
+                DIR *d;
+                struct dirent *de;
+
+                if (asprintf(&path, "%s/%s.wants", unit_path(), t) < 0)
+                        return -ENOMEM;
+
+                if (!(d = opendir(path))) {
+                        r = -errno;
+                        free(path);
+
+                        if (r == -ENOENT)
+                                continue;
+
+                        return r;
+                }
+
+                free(path);
+
+                while ((de = readdir(d))) {
+                        Unit *other;
+
+                        if (de->d_name[0] == '.')
+                                continue;
+
+                        assert(de->d_name[0]);
+
+                        if (de->d_name[strlen(de->d_name)-1] == '~')
+                                continue;
+
+                        if (asprintf(&path, "%s/%s.wants/%s", unit_path(), t, de->d_name) < 0) {
+                                closedir(d);
+                                return -ENOMEM;
+                        }
+
+                        r = manager_load_unit(u->meta.manager, path, &other);
+                        free(path);
+
+                        if (r < 0) {
+                                closedir(d);
+                                return r;
+                        }
+
+                        if ((r = unit_add_dependency(u, UNIT_WANTS, other)) < 0) {
+                                closedir(d);
+                                return r;
+                        }
+                }
+
+                closedir(d);
+        }
+
         return 0;
 }
index 7877a8c..d016f01 100644 (file)
@@ -328,7 +328,7 @@ static int config_parse_exec(
 
         n[k] = NULL;
 
-        if (!n[0] || n[0][0] != '/') {
+        if (!n[0] || !path_is_absolute(n[0])) {
                 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
                 strv_free(n);
                 return -EINVAL;
@@ -463,7 +463,7 @@ static char *build_path(const char *path, const char *filename) {
          * filename, unless the latter is absolute anyway or the
          * former isn't */
 
-        if (filename[0] == '/')
+        if (path_is_absolute(filename))
                 return strdup(filename);
 
         if (!(e = strrchr(path, '/')))
@@ -479,88 +479,73 @@ static char *build_path(const char *path, const char *filename) {
         return r;
 }
 
-static int open_follow(const char **filename, FILE **_f, Set *names) {
-        unsigned c;
+static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
+        unsigned c = 0;
         int fd, r;
         FILE *f;
-        char *n = NULL;
-        const char *fn;
+        char *id = NULL;
 
         assert(filename);
         assert(*filename);
         assert(_f);
         assert(names);
 
-        fn = *filename;
+        /* This will update the filename pointer if the loaded file is
+         * reached by a symlink. The old string will be freed. */
 
-        for (c = 0; c < FOLLOW_MAX; c++) {
+        for (;;) {
                 char *target, *k, *name;
 
+                if (c++ >= FOLLOW_MAX)
+                        return -ELOOP;
+
                 /* Add the file name we are currently looking at to
                  * the names of this unit */
-                name = file_name_from_path(fn);
-                if (!set_get(names, name)) {
+                name = file_name_from_path(*filename);
+                if (!(id = set_get(names, name))) {
 
-                        if (!(name = strdup(name))) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        if (!(id = strdup(name)))
+                                return -ENOMEM;
 
-                        if ((r = set_put(names, name)) < 0) {
-                                free(name);
-                                goto finish;
+                        if ((r = set_put(names, id)) < 0) {
+                                free(id);
+                                return r;
                         }
-
-                        free(name);
                 }
 
-                /* Try to open the file name, but don' if its a symlink */
-                fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-                if (fd >= 0 || errno != ELOOP)
+                /* Try to open the file name, but don't if its a symlink */
+                if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
                         break;
 
+                if (errno != ELOOP)
+                        return -errno;
+
                 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
-                if ((r = readlink_malloc(fn, &target)) < 0)
-                        goto finish;
+                if ((r = readlink_malloc(*filename, &target)) < 0)
+                        return r;
 
-                k = build_path(fn, target);
+                k = build_path(*filename, target);
                 free(target);
 
-                if (!k) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                free(n);
-                fn = n = k;
-        }
-
-        if (c >= FOLLOW_MAX) {
-                r = -ELOOP;
-                goto finish;
-        }
+                if (!k)
+                        return -ENOMEM;
 
-        if (fd < 0) {
-                r = -errno;
-                goto finish;
+                free(*filename);
+                *filename = k;
         }
 
         if (!(f = fdopen(fd, "r"))) {
                 r = -errno;
                 assert(close_nointr(fd) == 0);
-                goto finish;
+                return r;
         }
 
         *_f = f;
-        *filename = fn;
-        r = 0;
-
-finish:
-        free(n);
-        return r;
+        *_id = id;
+        return 0;
 }
 
-int unit_load_fragment(Unit *u) {
+static int load_from_path(Unit *u, const char *path) {
 
         static const char* const section_table[_UNIT_TYPE_MAX] = {
                 [UNIT_SERVICE]   = "Service",
@@ -627,14 +612,12 @@ int unit_load_fragment(Unit *u) {
 
 #undef EXEC_CONTEXT_CONFIG_ITEMS
 
-        char *t, *k;
-        int r;
         const char *sections[3];
-        Iterator i;
+        char *k;
+        int r;
         Set *symlink_names;
-
-        assert(u);
-        assert(u->meta.load_state == UNIT_STUB);
+        FILE *f;
+        char *filename, *id;
 
         sections[0] = "Meta";
         sections[1] = section_table[u->meta.type];
@@ -643,51 +626,69 @@ int unit_load_fragment(Unit *u) {
         if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
                 return -ENOMEM;
 
-        /* Try to find a name we can load this with */
-        SET_FOREACH(t, u->meta.names, i) {
-                FILE *f;
-                char *fn;
+        /* Instead of opening the path right away, we manually
+         * follow all symlinks and add their name to our unit
+         * name set while doing so */
+        if (!(filename = path_make_absolute(path, unit_path()))) {
+                r = -ENOMEM;
+                goto finish;
+        }
 
-                /* Clear the symlink name set first */
-                while ((k = set_steal_first(symlink_names)))
-                        free(k);
+        if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
+                if (r == -ENOENT)
+                        r = 0; /* returning 0 means: no suitable config file found */
 
-                /* Instead of opening the path right away, we manually
-                 * follow all symlinks and add their name to our unit
-                 * name set while doing so */
-                fn = t;
-                if ((r = open_follow((const char**) &fn, &f, symlink_names)) < 0) {
-                        if (r == -ENOENT)
-                                continue;
+                goto finish;
+        }
 
-                        goto finish;
-                }
+        /* Now, parse the file contents */
+        r = config_parse(filename, f, sections, items, u);
+        if (r < 0)
+                goto finish;
 
-                /* Now, parse the file contents */
-                r = config_parse(fn, f, sections, items, u);
-                if (fn != t)
-                        free(fn);
-                if (r < 0)
+        /* Let's try to add in all symlink names we found */
+        while ((k = set_steal_first(symlink_names))) {
+                if ((r = unit_add_name(u, k)) < 0)
                         goto finish;
 
-                /* Let's try to add in all symlink names we found */
-                while ((k = set_steal_first(symlink_names)))
-                        if ((r = unit_add_name(u, k)) < 0)
-                                goto finish;
+                if (id == k)
+                        assert_se(u->meta.id = set_get(u->meta.names, k));
 
-                /* Yay, we succeeded! Now let's call this our identifier */
-                u->meta.id = t;
-                goto finish;
+                free(k);
         }
 
+        free(u->meta.load_path);
+        u->meta.load_path = filename;
+        filename = NULL;
 
-        r = -ENOENT;
+        r = 1; /* returning 1 means: suitable config file found and loaded */
 
 finish:
         while ((k = set_steal_first(symlink_names)))
                 free(k);
-
         set_free(symlink_names);
+        free(filename);
+
+        return r;
+}
+
+int unit_load_fragment(Unit *u) {
+        int r = -ENOENT;
+
+        assert(u);
+        assert(u->meta.load_state == UNIT_STUB);
+
+        if (u->meta.load_path)
+                r = load_from_path(u, u->meta.load_path);
+        else {
+                Iterator i;
+                char *t;
+
+                /* Try to find a name we can load this with */
+                SET_FOREACH(t, u->meta.names, i)
+                        if ((r = load_from_path(u, t)) != 0)
+                                return r;
+        }
 
         return r;
 }
diff --git a/main.c b/main.c
index e139c4b..663e7e3 100644 (file)
--- a/main.c
+++ b/main.c
@@ -14,7 +14,7 @@ int main(int argc, char *argv[]) {
         Job *job = NULL;
         int r, retval = 1;
 
-        assert_se(chdir("test1") == 0);
+        assert_se(set_unit_path("test1") >= 0);
 
         if (!(m = manager_new()) < 0) {
                 log_error("Failed to allocate manager object: %s", strerror(ENOMEM));
@@ -26,10 +26,10 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
-                log_error("Failed to start default target: %s", strerror(-r));
-                goto finish;
-        }
+        /* if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) { */
+        /*         log_error("Failed to start default target: %s", strerror(-r)); */
+        /*         goto finish; */
+        /* } */
 
         printf("→ By units:\n");
         manager_dump_units(m, stdout, "\t");
index 819164c..682c7e7 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -830,16 +830,19 @@ static void dispatch_load_queue(Manager *m) {
         m->dispatching_load_queue = false;
 }
 
-int manager_load_unit(Manager *m, const char *name, Unit **_ret) {
+int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
         Unit *ret;
         int r;
+        const char *name;
 
         assert(m);
-        assert(name);
+        assert(path);
         assert(_ret);
 
         /* This will load the service information files, but not actually
-         * start any services or anything */
+         * start any services or anything. */
+
+        name = file_name_from_path(path);
 
         if ((ret = manager_get_unit(m, name))) {
                 *_ret = ret;
@@ -849,6 +852,13 @@ int manager_load_unit(Manager *m, const char *name, Unit **_ret) {
         if (!(ret = unit_new(m)))
                 return -ENOMEM;
 
+        if (is_path(path)) {
+                if (!(ret->meta.load_path = strdup(path))) {
+                        unit_free(ret);
+                        return -ENOMEM;
+                }
+        }
+
         if ((r = unit_add_name(ret, name)) < 0) {
                 unit_free(ret);
                 return r;
index 432d730..a17a6c2 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -58,7 +58,7 @@ void manager_free(Manager *m);
 Job *manager_get_job(Manager *m, uint32_t id);
 Unit *manager_get_unit(Manager *m, const char *name);
 
-int manager_load_unit(Manager *m, const char *name, Unit **_ret);
+int manager_load_unit(Manager *m, const char *path_or_name, Unit **_ret);
 int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret);
 
 void manager_dump_units(Manager *s, FILE *f, const char *prefix);
index 6754676..bf44815 100644 (file)
--- a/target.c
+++ b/target.c
@@ -19,7 +19,7 @@ static UnitActiveState target_active_state(Unit *u) {
 const UnitVTable target_vtable = {
         .suffix = ".target",
 
-        .init = unit_load_fragment,
+        .init = unit_load_fragment_and_dropin,
         .done = target_done,
 
         .active_state = target_active_state
index c14a340..85d7697 100644 (file)
@@ -12,7 +12,7 @@ int main(int argc, char *argv[]) {
         Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
         Job *j;
 
-        assert_se(chdir("test2") == 0);
+        assert_se(set_unit_path("test2") >= 0);
 
         assert_se(m = manager_new());
 
deleted file mode 100644 (file)
index b158a237048d0aeec7996e5a6f2d9a19c67dc1ea..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[Meta]
-Names=multiuser.target
-Wants=postfix.socket syslog.socket
-Description=Default Target
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..264d98085be915d3413d6c7ba7900eec5c441e30
--- /dev/null
@@ -0,0 +1 @@
+multiuser.target
\ No newline at end of file
diff --git a/test1/mail-transfer-agent.service b/test1/mail-transfer-agent.service
new file mode 120000 (symlink)
index 0000000..bca89da
--- /dev/null
@@ -0,0 +1 @@
+postfix.service
\ No newline at end of file
diff --git a/test1/mail-transfer-agent.socket b/test1/mail-transfer-agent.socket
new file mode 120000 (symlink)
index 0000000..daf3277
--- /dev/null
@@ -0,0 +1 @@
+postfix.socket
\ No newline at end of file
diff --git a/test1/multiuser.target b/test1/multiuser.target
new file mode 100644 (file)
index 0000000..3518242
--- /dev/null
@@ -0,0 +1,3 @@
+[Meta]
+Wants=syslog.socket
+Description=Multi-User Target
diff --git a/test1/multiuser.target.wants/mail-transfer-agent.socket b/test1/multiuser.target.wants/mail-transfer-agent.socket
new file mode 120000 (symlink)
index 0000000..3c3a2db
--- /dev/null
@@ -0,0 +1 @@
+../mail-transfer-agent.socket
\ No newline at end of file
diff --git a/unit.c b/unit.c
index 15401e2..c722717 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -6,6 +6,8 @@
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/poll.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #include "set.h"
 #include "unit.h"
@@ -206,6 +208,7 @@ void unit_free(Unit *u) {
                 bidi_set_free(u, u->meta.dependencies[d]);
 
         free(u->meta.description);
+        free(u->meta.load_path);
 
         while ((t = set_steal_first(u->meta.names)))
                 free(t);
@@ -338,6 +341,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, load_state_table[u->meta.load_state],
                 prefix, active_state_table[unit_active_state(u)]);
 
+        if (u->meta.load_path)
+                fprintf(f, "%s\tLoad Path: %s\n", prefix, u->meta.load_path);
+
         SET_FOREACH(t, u->meta.names, i)
                 fprintf(f, "%s\tName: %s\n", prefix, t);
 
@@ -805,3 +811,42 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
 
         return 0;
 }
+
+const char *unit_path(void) {
+        char *e;
+
+        if ((e = getenv("UNIT_PATH")))
+                if (path_is_absolute(e))
+                    return e;
+
+        return UNIT_PATH;
+}
+
+int set_unit_path(const char *p) {
+        char *cwd, *c;
+        int r;
+
+        /* This is mostly for debug purposes */
+
+        if (path_is_absolute(p)) {
+                if (!(c = strdup(p)))
+                        return -ENOMEM;
+        } else {
+                if (!(cwd = get_current_dir_name()))
+                        return -errno;
+
+                r = asprintf(&c, "%s/%s", cwd, p);
+                free(cwd);
+
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        if (setenv("UNIT_PATH", c, 0) < 0) {
+                r = -errno;
+                free(c);
+                return r;
+        }
+
+        return 0;
+}
diff --git a/unit.h b/unit.h
index 7803f31..cabf230 100644 (file)
--- a/unit.h
+++ b/unit.h
@@ -103,6 +103,7 @@ struct Meta {
         Set *dependencies[_UNIT_DEPENDENCY_MAX];
 
         char *description;
+        char *load_path; /* if loaded from a config file this is the primary path to it */
 
         /* If there is something to do with this unit, then this is
          * the job for it */
@@ -232,4 +233,8 @@ void unit_unwatch_timer(Unit *u, int *id);
 
 bool unit_job_is_applicable(Unit *u, JobType j);
 
+const char *unit_path(void);
+int set_unit_path(const char *p);
+
+
 #endif
diff --git a/util.c b/util.c
index f752a24..7c39353 100644 (file)
--- a/util.c
+++ b/util.c
@@ -443,3 +443,28 @@ char *file_name_from_path(const char *p) {
 
         return (char*) p;
 }
+
+bool path_is_absolute(const char *p) {
+        assert(p);
+
+        return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+
+        return !!strchr(p, '/');
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+        char *r;
+
+        assert(p);
+
+        if (path_is_absolute(p) || !prefix)
+                return strdup(p);
+
+        if (asprintf(&r, "%s/%s", prefix, p) < 0)
+                return NULL;
+
+        return r;
+}
diff --git a/util.h b/util.h
index 8cc6043..f14751e 100644 (file)
--- a/util.h
+++ b/util.h
@@ -94,5 +94,9 @@ char *strappend(const char *s, const char *suffix);
 int readlink_malloc(const char *p, char **r);
 
 char *file_name_from_path(const char *p);
+bool is_path(const char *p);
+
+bool path_is_absolute(const char *p);
+char *path_make_absolute(const char *p, const char *prefix);
 
 #endif