From: Lennart Poettering Date: Sat, 13 Feb 2010 00:07:02 +0000 (+0100) Subject: config: implement search path logic X-Git-Tag: 0.git+20100605+dfd8ee-1~306 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=036643a247c659db8e1b3df1778d51553a816ec9;p=systemd config: implement search path logic --- diff --git a/Makefile.am b/Makefile.am index 8b481a6b..3fc221c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,9 +17,15 @@ ACLOCAL_AMFLAGS = -I m4 +pkgsysconfdir=$(sysconfdir)/systemd + AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ - -DUNIT_PATH=\"/tmp/does/not/exist\" + -DSYSTEM_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/system\" \ + -DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \ + -DSYSTEM_SYSVINIT_PATH=\"$(sysconfdir)/init.d\" \ + -DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \ + -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\" sbin_PROGRAMS = \ systemd diff --git a/dbus.c b/dbus.c index bfec0f97..bef8bb88 100644 --- a/dbus.c +++ b/dbus.c @@ -378,7 +378,7 @@ int bus_init(Manager *m) { dbus_connection_set_change_sigpipe(FALSE); dbus_error_init(&error); - if (!(m->bus = dbus_bus_get_private(m->running_as == MANAGER_USER ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { + if (!(m->bus = dbus_bus_get_private(m->running_as == MANAGER_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { log_error("Failed to get D-Bus connection: %s", error.message); dbus_error_free(&error); return -ECONNREFUSED; diff --git a/load-dropin.c b/load-dropin.c index 13d7bc8f..af95d37a 100644 --- a/load-dropin.c +++ b/load-dropin.c @@ -25,6 +25,7 @@ #include "unit.h" #include "load-dropin.h" #include "log.h" +#include "strv.h" int unit_load_dropin(Unit *u) { Iterator i; @@ -39,52 +40,56 @@ int unit_load_dropin(Unit *u) { char *path; DIR *d; struct dirent *de; + char **p; - if (asprintf(&path, "%s/%s.wants", unit_path(), t) < 0) - return -ENOMEM; + STRV_FOREACH(p, u->meta.manager->unit_path) { - if (!(d = opendir(path))) { - r = -errno; - free(path); + if (asprintf(&path, "%s/%s.wants", *p, t) < 0) + return -ENOMEM; - if (r == -ENOENT) - continue; + if (!(d = opendir(path))) { + r = -errno; + free(path); - return r; - } + if (r == -ENOENT) + continue; - free(path); + return r; + } - while ((de = readdir(d))) { - if (de->d_name[0] == '.') - continue; + free(path); - assert(de->d_name[0]); + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; - if (de->d_name[strlen(de->d_name)-1] == '~') - continue; + assert(de->d_name[0]); - if (asprintf(&path, "%s/%s.wants/%s", unit_path(), t, de->d_name) < 0) { - closedir(d); - return -ENOMEM; - } + if (de->d_name[strlen(de->d_name)-1] == '~') + continue; - if (!unit_name_is_valid(de->d_name)) { - log_info("Name of %s is not a valid unit name. Ignoring.", path); - free(path); - continue; - } + if (asprintf(&path, "%s/%s.wants/%s", *p, t, de->d_name) < 0) { + closedir(d); + return -ENOMEM; + } - r = unit_add_dependency_by_name(u, UNIT_WANTS, path); - free(path); + if (!unit_name_is_valid(de->d_name)) { + log_info("Name of %s is not a valid unit name. Ignoring.", path); + free(path); + continue; + } - if (r < 0) { - closedir(d); - return r; + r = unit_add_dependency_by_name(u, UNIT_WANTS, path); + free(path); + + if (r < 0) { + closedir(d); + return r; + } } - } - closedir(d); + closedir(d); + } } return 0; diff --git a/load-fragment.c b/load-fragment.c index 95e6cc3f..64e66f31 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -1128,7 +1128,7 @@ static int load_from_path(Unit *u, const char *path) { int r; Set *symlink_names; FILE *f; - char *filename, *id; + char *filename = NULL, *id; sections[0] = "Meta"; sections[1] = section_table[u->meta.type]; @@ -1137,18 +1137,56 @@ static int load_from_path(Unit *u, const char *path) { if (!(symlink_names = set_new(string_hash_func, string_compare_func))) return -ENOMEM; - /* 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; - } + if (path_is_absolute(path)) { + + if (!(filename = strdup(path))) { + r = -ENOMEM; + goto finish; + } + + if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { + free(filename); + filename = NULL; - if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { - if (r == -ENOENT) - r = 0; /* returning 0 means: no suitable config file found */ + if (r != -ENOENT) + goto finish; + } + + } else { + char **p; + + STRV_FOREACH(p, u->meta.manager->unit_path) { + + /* 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, *p))) { + r = -ENOMEM; + goto finish; + } + + if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { + char *sn; + + free(filename); + filename = NULL; + + if (r != -ENOENT) + goto finish; + + /* Empty the symlink names for the next run */ + while ((sn = set_steal_first(symlink_names))) + free(sn); + + continue; + } + + break; + } + } + if (!filename) { + r = 0; /* returning 0 means: no suitable config file found */ goto finish; } diff --git a/manager.c b/manager.c index 25bc7528..bec13988 100644 --- a/manager.c +++ b/manager.c @@ -70,6 +70,176 @@ static int manager_setup_signals(Manager *m) { return 0; } +static char** session_dirs(void) { + const char *home, *e; + char *config_home = NULL, *data_home = NULL; + char **config_dirs = NULL, **data_dirs = NULL; + char **r = NULL, **t; + + /* Implement the mechanisms defined in + * + * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html + * + * We look in both the config and the data dirs because we + * want to encourage that distributors ship their unit files + * as data, and allow overriding as configuration. + */ + + home = getenv("HOME"); + + if ((e = getenv("XDG_CONFIG_HOME"))) { + if (asprintf(&config_home, "%s/systemd/session", e) < 0) + goto fail; + + } else if (home) { + if (asprintf(&config_home, "%s/.config/systemd/session", home) < 0) + goto fail; + } + + if ((e = getenv("XDG_CONFIG_DIRS"))) + config_dirs = strv_split(e, ":"); + else + config_dirs = strv_new("/etc/xdg", NULL); + + if (!config_dirs) + goto fail; + + if ((e = getenv("XDG_DATA_HOME"))) { + if (asprintf(&data_home, "%s/systemd/session", e) < 0) + goto fail; + + } else if (home) { + if (asprintf(&data_home, "%s/.local/share/systemd/session", home) < 0) + goto fail; + } + + if ((e = getenv("XDG_DATA_DIRS"))) + data_dirs = strv_split(e, ":"); + else + data_dirs = strv_new("/usr/local/share", "/usr/share", NULL); + + if (!data_dirs) + goto fail; + + /* Now merge everything we found. */ + if (config_home) { + if (!(t = strv_append(r, config_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!(t = strv_merge_concat(r, config_dirs, "/systemd/session"))) + goto finish; + strv_free(r); + r = t; + + if (!(t = strv_append(r, SESSION_CONFIG_UNIT_PATH))) + goto fail; + strv_free(r); + r = t; + + if (data_home) { + if (!(t = strv_append(r, data_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!(t = strv_merge_concat(r, data_dirs, "/systemd/session"))) + goto fail; + strv_free(r); + r = t; + + if (!(t = strv_append(r, SESSION_DATA_UNIT_PATH))) + goto fail; + strv_free(r); + r = t; + + if (!strv_path_make_absolute_cwd(r)) + goto fail; + +finish: + free(config_home); + strv_free(config_dirs); + free(data_home); + strv_free(data_dirs); + + return r; + +fail: + strv_free(r); + r = NULL; + goto finish; +} + +static int manager_find_paths(Manager *m) { + const char *e; + char *t; + assert(m); + + /* First priority is whatever has been passed to us via env + * vars */ + if ((e = getenv("SYSTEMD_UNIT_PATH"))) + if (!(m->unit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(m->unit_path)) { + + /* Nothing is set, so let's figure something out. */ + strv_free(m->unit_path); + + if (m->running_as == MANAGER_SESSION) { + if (!(m->unit_path = session_dirs())) + return -ENOMEM; + } else + if (!(m->unit_path = strv_new( + SYSTEM_CONFIG_UNIT_PATH, /* /etc/systemd/system/ */ + SYSTEM_DATA_UNIT_PATH, /* /lib/systemd/system/ */ + NULL))) + return -ENOMEM; + } + + /* FIXME: This should probably look for MANAGER_INIT, and exclude MANAGER_SYSTEM */ + if (m->running_as != MANAGER_SESSION) { + /* /etc/init.d/ compativility does not matter to users */ + + if ((e = getenv("SYSTEMD_SYSVINIT_PATH"))) + if (!(m->sysvinit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(m->sysvinit_path)) { + strv_free(m->sysvinit_path); + + if (!(m->sysvinit_path = strv_new( + SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ + NULL))) + return -ENOMEM; + } + } + + strv_uniq(m->unit_path); + strv_uniq(m->sysvinit_path); + + assert(!strv_isempty(m->unit_path)); + if (!(t = strv_join(m->unit_path, "\n\t"))) + return -ENOMEM; + log_debug("Looking for unit files in:\n\t%s", t); + free(t); + + if (!strv_isempty(m->sysvinit_path)) { + + if (!(t = strv_join(m->sysvinit_path, "\n\t"))) + return -ENOMEM; + + log_debug("Looking for SysV init scripts in:\n\t%s", t); + free(t); + } else + log_debug("Ignoring SysV init scripts."); + + return 0; +} + Manager* manager_new(void) { Manager *m; @@ -81,13 +251,16 @@ Manager* manager_new(void) { else if (getuid() == 0) m->running_as = MANAGER_SYSTEM; else - m->running_as = MANAGER_USER; + m->running_as = MANAGER_SESSION; log_debug("systemd running in %s mode.", manager_running_as_to_string(m->running_as)); m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ + if (manager_find_paths(m) < 0) + goto fail; + if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -146,6 +319,9 @@ void manager_free(Manager *m) { if (m->signal_watch.fd >= 0) close_nointr(m->signal_watch.fd); + strv_free(m->unit_path); + strv_free(m->sysvinit_path); + free(m); } @@ -1360,7 +1536,7 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { [MANAGER_INIT] = "init", [MANAGER_SYSTEM] = "system", - [MANAGER_USER] = "user" + [MANAGER_SESSION] = "session" }; DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs); diff --git a/manager.h b/manager.h index 45fb0944..900c3fa4 100644 --- a/manager.h +++ b/manager.h @@ -35,7 +35,7 @@ typedef struct Watch Watch; typedef enum ManagerRunningAs { MANAGER_INIT, /* root and pid=1 */ MANAGER_SYSTEM, /* root and pid!=1 */ - MANAGER_USER, /* non-root */ + MANAGER_SESSION, /* non-root */ _MANAGER_RUNNING_AS_MAX, _MANAGER_RUNNING_AS_INVALID = -1 } ManagerRunningAs; @@ -123,6 +123,9 @@ struct Manager { Watch signal_watch; + char **unit_path; + char **sysvinit_path; + /* Data specific to the device subsystem */ struct udev* udev; struct udev_monitor* udev_monitor; diff --git a/unit.c b/unit.c index cb9fe7cd..b8a1d8bb 100644 --- a/unit.c +++ b/unit.c @@ -972,16 +972,6 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name) { 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; @@ -1002,7 +992,7 @@ int set_unit_path(const char *p) { return -ENOMEM; } - if (setenv("UNIT_PATH", c, 0) < 0) { + if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) { r = -errno; free(c); return r; diff --git a/unit.h b/unit.h index e4034fd7..322e4aed 100644 --- a/unit.h +++ b/unit.h @@ -282,7 +282,6 @@ void unit_unwatch_timer(Unit *u, Watch *w); bool unit_job_is_applicable(Unit *u, JobType j); -const char *unit_path(void); int set_unit_path(const char *p); char *unit_name_escape_path(const char *prefix, const char *path, const char *suffix);