summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--load-dropin.c61
-rw-r--r--load-fragment.c171
-rw-r--r--main.c10
-rw-r--r--manager.c16
-rw-r--r--manager.h2
-rw-r--r--target.c2
-rw-r--r--test-engine.c2
l---------[-rw-r--r--]test1/default.target5
l---------test1/mail-transfer-agent.service1
l---------test1/mail-transfer-agent.socket1
-rw-r--r--test1/multiuser.target3
l---------test1/multiuser.target.wants/mail-transfer-agent.socket1
-rw-r--r--unit.c45
-rw-r--r--unit.h5
-rw-r--r--util.c25
-rw-r--r--util.h4
17 files changed, 255 insertions, 101 deletions
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 <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;
}
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
index b158a2370..264d98085 100644..120000
--- a/test1/default.target
+++ b/test1/default.target
@@ -1,4 +1 @@
-[Meta]
-Names=multiuser.target
-Wants=postfix.socket syslog.socket
-Description=Default Target
+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 <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 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