From: Lennart Poettering Date: Tue, 26 Jan 2010 23:15:56 +0000 (+0100) Subject: implement drop-in directories X-Git-Tag: v1~812 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=0301abf48ed3be921c33d409c73b554435cf6378 implement drop-in directories --- diff --git a/Makefile b/Makefile index 9a862207c..119d3dc66 100644 --- 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= \ diff --git a/load-dropin.c b/load-dropin.c index 3023cdae6..b975d83f0 100644 --- a/load-dropin.c +++ b/load-dropin.c @@ -1,11 +1,72 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ +#include +#include + +#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; } diff --git a/load-fragment.c b/load-fragment.c index 7877a8c4a..d016f01d1 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -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 e139c4b19..663e7e3af 100644 --- 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"); diff --git a/manager.c b/manager.c index 819164ca2..682c7e7f2 100644 --- 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; diff --git a/manager.h b/manager.h index 432d730d7..a17a6c2f6 100644 --- 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); diff --git a/target.c b/target.c index 6754676b9..bf448158c 100644 --- 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 diff --git a/test-engine.c b/test-engine.c index c14a3402d..85d7697c3 100644 --- a/test-engine.c +++ b/test-engine.c @@ -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()); diff --git a/test1/default.target b/test1/default.target deleted file mode 100644 index b158a2370..000000000 --- a/test1/default.target +++ /dev/null @@ -1,4 +0,0 @@ -[Meta] -Names=multiuser.target -Wants=postfix.socket syslog.socket -Description=Default Target diff --git a/test1/default.target b/test1/default.target new file mode 120000 index 000000000..264d98085 --- /dev/null +++ b/test1/default.target @@ -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 index 000000000..bca89da28 --- /dev/null +++ b/test1/mail-transfer-agent.service @@ -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 index 000000000..daf3277d0 --- /dev/null +++ b/test1/mail-transfer-agent.socket @@ -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 index 000000000..35182421c --- /dev/null +++ b/test1/multiuser.target @@ -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 index 000000000..3c3a2db35 --- /dev/null +++ b/test1/multiuser.target.wants/mail-transfer-agent.socket @@ -0,0 +1 @@ +../mail-transfer-agent.socket \ No newline at end of file diff --git a/unit.c b/unit.c index 15401e24f..c722717fe 100644 --- a/unit.c +++ b/unit.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #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 7803f3183..cabf23014 100644 --- 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 f752a248e..7c3935353 100644 --- 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 8cc6043fc..f14751ea5 100644 --- 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