Commit 0301abf4 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

implement drop-in directories

parent 87f0e418
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= \
......
/*-*- 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;
}
......@@ -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;
}
......@@ -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");
......
......@@ -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;
......
......@@ -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);
......
......@@ -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
......
......@@ -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());
......
[Meta]
Names=multiuser.target
Wants=postfix.socket syslog.socket
Description=Default Target
multiuser.target
\ No newline at end of file
postfix.service
\ No newline at end of file
postfix.socket
\ No newline at end of file
[Meta]
Wants=syslog.socket
Description=Multi-User Target
../mail-transfer-agent.socket
\ No newline at end of file
......@@ -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;
}
......@@ -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
......@@ -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;
}
......@@ -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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment