Commit 07719a21 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

manager: rework generator logic

Previously generated units were always placed at the end of the search
path. With this change there will be three unit dirs instead of one, to
place generated entries at the beginning, in the middle and at the end
of the search path:

beginning: for units that need to override all configuration, regardless
of user or vendor. Example use: system-update-generator uses this to
temporarily redirect default.target.

middle: for units that need to override vendor configuration, but not
vendor configuration. Example use: /etc/fstab should override vendor
supplied configuration (think /tmp), but should not override native user
configuration.

end: does not override anything but is available as well. Possible usage
might be to convert D-Bus bus service files to native units but allowing
vendor supplied native units to win.
parent 7c4c5f68
......@@ -299,9 +299,6 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
goto fail;
if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
goto fail;
if ((r = manager_setup_signals(m)) < 0)
goto fail;
......@@ -637,6 +634,14 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
manager_run_generators(m);
r = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (r < 0)
return r;
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
......@@ -649,12 +654,15 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
r = manager_enumerate(m);
/* Second, deserialize if there is something to deserialize */
if (serialization)
if ((q = manager_deserialize(m, serialization, fds)) < 0)
if (serialization) {
q = manager_deserialize(m, serialization, fds);
if (q < 0)
r = q;
}
/* Third, fire things up! */
if ((q = manager_coldplug(m)) < 0)
q = manager_coldplug(m);
if (q < 0)
r = q;
if (serialization) {
......@@ -1871,18 +1879,21 @@ int manager_reload(Manager *m) {
assert(m);
if ((r = manager_open_serialization(m, &f)) < 0)
r = manager_open_serialization(m, &f);
if (r < 0)
return r;
m->n_reloading ++;
if (!(fds = fdset_new())) {
fds = fdset_new();
if (!fds) {
m->n_reloading --;
r = -ENOMEM;
goto finish;
}
if ((r = manager_serialize(m, f, fds)) < 0) {
r = manager_serialize(m, f, fds);
if (r < 0) {
m->n_reloading --;
goto finish;
}
......@@ -1896,29 +1907,37 @@ int manager_reload(Manager *m) {
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
manager_undo_generators(m);
/* Find new unit paths */
lookup_paths_free(&m->lookup_paths);
if ((q = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
r = q;
/* Find new unit paths */
manager_run_generators(m);
q = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (q < 0)
r = q;
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
if ((q = manager_enumerate(m)) < 0)
q = manager_enumerate(m);
if (q < 0)
r = q;
/* Second, deserialize our stored data */
if ((q = manager_deserialize(m, f, fds)) < 0)
q = manager_deserialize(m, f, fds);
if (q < 0)
r = q;
fclose(f);
f = NULL;
/* Third, fire things up! */
if ((q = manager_coldplug(m)) < 0)
q = manager_coldplug(m);
if (q < 0)
r = q;
assert(m->n_reloading > 0);
......@@ -2030,17 +2049,76 @@ void manager_check_finished(Manager *m) {
format_timespan(sum, sizeof(sum), total_usec));
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
char *p;
int r;
assert(m);
assert(generator);
assert(name);
if (*generator)
return 0;
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
p = strappend("/run/systemd/", name);
if (!p) {
log_error("Out of memory");
return -ENOMEM;
}
r = mkdir_p(p, 0755);
if (r < 0) {
log_error("Failed to create generator directory: %s", strerror(-r));
free(p);
return r;
}
} else {
p = join("/tmp/systemd-", name, ".XXXXXX", NULL);
if (!p) {
log_error("Out of memory");
return -ENOMEM;
}
if (!mkdtemp(p)) {
free(p);
log_error("Failed to create generator directory: %m");
return -errno;
}
}
*generator = p;
return 0;
}
static void trim_generator_dir(Manager *m, char **generator) {
assert(m);
assert(generator);
if (!*generator)
return;
if (rmdir(*generator) >= 0) {
free(*generator);
*generator = NULL;
}
return;
}
void manager_run_generators(Manager *m) {
DIR *d = NULL;
const char *generator_path;
const char *argv[3];
const char *argv[5];
mode_t u;
int r;
assert(m);
generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
if (!(d = opendir(generator_path))) {
d = opendir(generator_path);
if (!d) {
if (errno == ENOENT)
return;
......@@ -2048,79 +2126,57 @@ void manager_run_generators(Manager *m) {
return;
}
if (!m->generator_unit_path) {
const char *p;
char user_path[] = "/tmp/systemd-generator-XXXXXX";
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
p = "/run/systemd/generator";
if (mkdir_p(p, 0755) < 0) {
log_error("Failed to create generator directory: %m");
goto finish;
}
r = create_generator_dir(m, &m->generator_unit_path, "generator");
if (r < 0)
goto finish;
} else {
if (!(p = mkdtemp(user_path))) {
log_error("Failed to create generator directory: %m");
goto finish;
}
}
r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
if (r < 0)
goto finish;
if (!(m->generator_unit_path = strdup(p))) {
log_error("Failed to allocate generator unit path.");
goto finish;
}
}
r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
if (r < 0)
goto finish;
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
argv[1] = m->generator_unit_path;
argv[2] = NULL;
argv[2] = m->generator_unit_path_early;
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
u = umask(0022);
execute_directory(generator_path, d, (char**) argv);
umask(u);
if (rmdir(m->generator_unit_path) >= 0) {
/* Uh? we were able to remove this dir? I guess that
* means the directory was empty, hence let's shortcut
* this */
free(m->generator_unit_path);
m->generator_unit_path = NULL;
goto finish;
}
if (!strv_find(m->lookup_paths.unit_path, m->generator_unit_path)) {
char **l;
if (!(l = strv_append(m->lookup_paths.unit_path, m->generator_unit_path))) {
log_error("Failed to add generator directory to unit search path: %m");
goto finish;
}
strv_free(m->lookup_paths.unit_path);
m->lookup_paths.unit_path = l;
log_debug("Added generator unit path %s to search path.", m->generator_unit_path);
}
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
trim_generator_dir(m, &m->generator_unit_path_late);
finish:
if (d)
closedir(d);
}
void manager_undo_generators(Manager *m) {
static void remove_generator_dir(Manager *m, char **generator) {
assert(m);
assert(generator);
if (!m->generator_unit_path)
if (!*generator)
return;
strv_remove(m->lookup_paths.unit_path, m->generator_unit_path);
rm_rf(m->generator_unit_path, false, true, false);
strv_remove(m->lookup_paths.unit_path, *generator);
rm_rf(*generator, false, true, false);
free(m->generator_unit_path);
m->generator_unit_path = NULL;
free(*generator);
*generator = NULL;
}
void manager_undo_generators(Manager *m) {
assert(m);
remove_generator_dir(m, &m->generator_unit_path);
remove_generator_dir(m, &m->generator_unit_path_early);
remove_generator_dir(m, &m->generator_unit_path_late);
}
int manager_set_default_controllers(Manager *m, char **controllers) {
......@@ -2146,18 +2202,17 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
assert(m);
for (i = 0; i < RLIMIT_NLIMITS; i++) {
if (default_rlimit[i]) {
m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1);
if (!default_rlimit[i])
continue;
if (!m->rlimit[i])
return -ENOMEM;
}
m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1);
if (!m->rlimit[i])
return -ENOMEM;
}
return 0;
}
void manager_recheck_journal(Manager *m) {
Unit *u;
......
......@@ -152,6 +152,8 @@ struct Manager {
dual_timestamp finish_timestamp;
char *generator_unit_path;
char *generator_unit_path_early;
char *generator_unit_path_late;
/* Data specific to the device subsystem */
struct udev* udev;
......
......@@ -240,8 +240,8 @@ int main(int argc, char *argv[]) {
int r = EXIT_SUCCESS;
unsigned n = 0;
if (argc > 2) {
log_error("This program takes one or no arguments.");
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
......
......@@ -498,8 +498,8 @@ finish:
int main(int argc, char *argv[]) {
int r;
if (argc > 2) {
log_error("This program takes one or no arguments.");
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
......@@ -510,8 +510,6 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
log_set_max_level(LOG_DEBUG);
umask(0022);
r = parse_fstab();
......
......@@ -38,8 +38,8 @@ static int add_symlink(const char *fservice, const char *tservice) {
assert(fservice);
assert(tservice);
asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice);
asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice);
from = strappend(SYSTEM_DATA_UNIT_PATH "/", fservice);
to = join(arg_dest,"/getty.target.wants/", tservice, NULL);
if (!from || !to) {
log_error("Out of memory");
......@@ -99,20 +99,20 @@ int main(int argc, char *argv[]) {
char *active;
const char *j;
if (argc > 2) {
log_error("This program takes one or no arguments.");
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[1];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
umask(0022);
if (argc > 1)
arg_dest = argv[1];
if (detect_container(NULL) > 0) {
log_debug("Automatically adding console shell.");
......
......@@ -66,7 +66,6 @@ static int add_symlink(const char *service, const char *where) {
}
finish:
free(from);
free(to);
......@@ -83,20 +82,21 @@ static bool file_is_executable(const char *f) {
}
int main(int argc, char *argv[]) {
int r = EXIT_SUCCESS;
if (argc > 2) {
log_error("This program takes one or no arguments.");
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[1];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
if (argc > 1)
arg_dest = argv[1];
umask(0022);
if (file_is_executable(SCRIPT_PATH_START)) {
log_debug("Automatically adding rc-local.service.");
......
......@@ -60,7 +60,8 @@ static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope)
return lookup_paths_init(paths,
scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
scope == UNIT_FILE_USER);
scope == UNIT_FILE_USER,
NULL, NULL, NULL);
}
static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
......
......@@ -34,7 +34,8 @@
int user_config_home(char **config_home) {
const char *e;
if ((e = getenv("XDG_CONFIG_HOME"))) {
e = getenv("XDG_CONFIG_HOME");
if (e) {
if (asprintf(config_home, "%s/systemd/user", e) < 0)
return -ENOMEM;
......@@ -42,7 +43,8 @@ int user_config_home(char **config_home) {
} else {
const char *home;
if ((home = getenv("HOME"))) {
home = getenv("HOME");
if (home) {
if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
return -ENOMEM;
......@@ -53,7 +55,11 @@ int user_config_home(char **config_home) {
return 0;
}
static char** user_dirs(void) {
static char** user_dirs(
const char *generator,
const char *generator_early,
const char *generator_late) {
const char * const config_unit_paths[] = {
USER_CONFIG_UNIT_PATH,
"/etc/systemd/user",
......@@ -89,15 +95,19 @@ static char** user_dirs(void) {
home = getenv("HOME");
if ((e = getenv("XDG_CONFIG_DIRS")))
if (!(config_dirs = strv_split(e, ":")))
e = getenv("XDG_CONFIG_DIRS");
if (e) {
config_dirs = strv_split(e, ":");
if (!config_dirs)
goto fail;
}
/* We don't treat /etc/xdg/systemd here as the spec
* suggests because we assume that that is a link to
* /etc/systemd/ anyway. */
if ((e = getenv("XDG_DATA_HOME"))) {
e = getenv("XDG_DATA_HOME");
if (e) {
if (asprintf(&data_home, "%s/systemd/user", e) < 0)
goto fail;
......@@ -116,57 +126,87 @@ static char** user_dirs(void) {
(void) symlink("../../../.config/systemd/user", data_home);
}
if ((e = getenv("XDG_DATA_DIRS")))
e = getenv("XDG_DATA_DIRS");
if (e)
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 (generator_early) {
t = strv_append(r, generator_early);
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (config_home) {
if (!(t = strv_append(r, config_home)))
t = strv_append(r, config_home);
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(config_dirs)) {
if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
t = strv_merge_concat(r, config_dirs, "/systemd/user");
if (!t)
goto finish;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) config_unit_paths)))
t = strv_merge(r, (char**) config_unit_paths);
if (!t)
goto fail;
strv_free(r);
r = t;
if (generator) {
t = strv_append(r, generator);
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (data_home) {
if (!(t = strv_append(r, data_home)))
t = strv_append(r, data_home);
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (!strv_isempty(data_dirs)) {
if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
t = strv_merge_concat(r, data_dirs, "/systemd/user");
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (!(t = strv_merge(r, (char**) data_unit_paths)))
t = strv_merge(r, (char**) data_unit_paths);
if (!t)
goto fail;
strv_free(r);
r = t;
if (generator_late) {
t = strv_append(r, generator_late);
if (!t)
goto fail;
strv_free(r);
r = t;
}
if (!path_strv_make_absolute_cwd(r))
goto fail;
goto fail;
finish:
free(config_home);
......@@ -182,7 +222,14 @@ fail:
goto finish;
}
int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
int lookup_paths_init(
LookupPaths *p,
ManagerRunningAs running_as,
bool personal,
const char *generator,
const char *generator_early,
const char *generator_late) {
const char *e;
char *t;
......@@ -190,64 +237,83 @@ int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal