diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/manager.c | 47 | ||||
-rw-r--r-- | src/core/manager.h | 3 | ||||
-rw-r--r-- | src/core/shutdown.c | 4 | ||||
-rw-r--r-- | src/shared/path-lookup.c | 128 | ||||
-rw-r--r-- | src/shared/path-lookup.h | 2 | ||||
-rw-r--r-- | src/shared/util.c | 122 | ||||
-rw-r--r-- | src/shared/util.h | 2 | ||||
-rw-r--r-- | src/sleep/sleep.c | 5 | ||||
-rw-r--r-- | src/test/test-util.c | 27 |
9 files changed, 210 insertions, 130 deletions
diff --git a/src/core/manager.c b/src/core/manager.c index 4918091126..e53f22215d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -93,6 +93,8 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); +static int manager_run_generators(Manager *m); +static void manager_undo_generators(Manager *m); static int manager_watch_jobs_in_progress(Manager *m) { usec_t next; @@ -1086,8 +1088,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); dual_timestamp_get(&m->generators_start_timestamp); - manager_run_generators(m); + r = manager_run_generators(m); dual_timestamp_get(&m->generators_finish_timestamp); + if (r < 0) + return r; r = lookup_paths_init( &m->lookup_paths, m->running_as, true, @@ -2517,7 +2521,9 @@ int manager_reload(Manager *m) { lookup_paths_free(&m->lookup_paths); /* Find new unit paths */ - manager_run_generators(m); + q = manager_run_generators(m); + if (q < 0 && r >= 0) + r = q; q = lookup_paths_init( &m->lookup_paths, m->running_as, true, @@ -2525,19 +2531,19 @@ int manager_reload(Manager *m) { m->generator_unit_path, m->generator_unit_path_early, m->generator_unit_path_late); - if (q < 0) + if (q < 0 && r >= 0) r = q; manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ q = manager_enumerate(m); - if (q < 0) + if (q < 0 && r >= 0) r = q; /* Second, deserialize our stored data */ q = manager_deserialize(m, f, fds); - if (q < 0) + if (q < 0 && r >= 0) r = q; fclose(f); @@ -2545,12 +2551,12 @@ int manager_reload(Manager *m) { /* Re-register notify_fd as event source */ q = manager_setup_notify(m); - if (q < 0) + if (q < 0 && r >= 0) r = q; /* Third, fire things up! */ q = manager_coldplug(m); - if (q < 0) + if (q < 0 && r >= 0) r = q; assert(m->n_reloading > 0); @@ -2775,27 +2781,33 @@ static void trim_generator_dir(Manager *m, char **generator) { return; } -void manager_run_generators(Manager *m) { - const char *generator_path; +static int manager_run_generators(Manager *m) { + _cleanup_free_ char **paths = NULL; const char *argv[5]; + char **path; int r; assert(m); if (m->test_run) - return; + return 0; - generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; + paths = generator_paths(m->running_as); + if (!paths) + return log_oom(); /* Optimize by skipping the whole process by not creating output directories * if no generators are found. */ - if (access(generator_path, F_OK) != 0) { + STRV_FOREACH(path, paths) { + r = access(*path, F_OK); + if (r == 0) + goto found; if (errno != ENOENT) - log_error_errno(errno, "Failed to open generator directory %s: %m", - generator_path); - return; + log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); } + return 0; + found: r = create_generator_dir(m, &m->generator_unit_path, "generator"); if (r < 0) goto finish; @@ -2815,12 +2827,13 @@ void manager_run_generators(Manager *m) { argv[4] = NULL; RUN_WITH_UMASK(0022) - execute_directory(generator_path, DEFAULT_TIMEOUT_USEC, (char**) argv); + execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); finish: 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); + return r; } static void remove_generator_dir(Manager *m, char **generator) { @@ -2837,7 +2850,7 @@ static void remove_generator_dir(Manager *m, char **generator) { *generator = NULL; } -void manager_undo_generators(Manager *m) { +static void manager_undo_generators(Manager *m) { assert(m); remove_generator_dir(m, &m->generator_unit_path); diff --git a/src/core/manager.h b/src/core/manager.h index ab75f902e5..19fb0a901d 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -349,9 +349,6 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name); void manager_check_finished(Manager *m); -void manager_run_generators(Manager *m); -void manager_undo_generators(Manager *m); - void manager_recheck_journal(Manager *m); void manager_set_show_status(Manager *m, ShowStatus mode); diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 4cbdf12587..71f001ac13 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -49,6 +49,7 @@ #include "cgroup-util.h" #include "def.h" #include "switch-root.h" +#include "strv.h" #define FINALIZE_ATTEMPTS 50 @@ -159,6 +160,7 @@ int main(int argc, char *argv[]) { char *arguments[3]; unsigned retries; int cmd, r; + static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; log_parse_environment(); r = parse_argv(argc, argv); @@ -308,7 +310,7 @@ int main(int argc, char *argv[]) { arguments[0] = NULL; arguments[1] = arg_verb; arguments[2] = NULL; - execute_directory(SYSTEM_SHUTDOWN_PATH, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); if (!in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0) { diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 051f1a4835..1e5bb858e8 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -78,6 +78,33 @@ int user_runtime_dir(char **runtime_dir) { return 0; } +static int user_data_home_dir(char **dir, const char *suffix) { + const char *e; + char *res; + + /* We don't treat /etc/xdg/systemd here as the spec + * suggests because we assume that that is a link to + * /etc/systemd/ anyway. */ + + e = getenv("XDG_DATA_HOME"); + if (e) + res = strappend(e, suffix); + else { + const char *home; + + home = getenv("HOME"); + if (home) + res = strjoin(home, "/.local/share", suffix, NULL); + else + return 0; + } + if (!res) + return -ENOMEM; + + *dir = res; + return 0; +} + static char** user_dirs( const char *generator, const char *generator_early, @@ -100,10 +127,12 @@ static char** user_dirs( NULL }; - const char *home, *e; + const char *e; _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL; _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; - char **r = NULL; + _cleanup_free_ char **res = NULL; + char **tmp; + int r; /* Implement the mechanisms defined in * @@ -115,33 +144,21 @@ static char** user_dirs( */ if (user_config_home(&config_home) < 0) - goto fail; + return NULL; if (user_runtime_dir(&runtime_dir) < 0) - goto fail; - - home = getenv("HOME"); + return NULL; e = getenv("XDG_CONFIG_DIRS"); if (e) { config_dirs = strv_split(e, ":"); if (!config_dirs) - goto fail; + return NULL; } - /* We don't treat /etc/xdg/systemd here as the spec - * suggests because we assume that that is a link to - * /etc/systemd/ anyway. */ - - e = getenv("XDG_DATA_HOME"); - if (e) { - if (asprintf(&data_home, "%s/systemd/user", e) < 0) - goto fail; - - } else if (home) { - if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0) - goto fail; - } + r = user_data_home_dir(&data_home, "/systemd/user"); + if (r < 0) + return NULL; e = getenv("XDG_DATA_DIRS"); if (e) @@ -151,58 +168,71 @@ static char** user_dirs( "/usr/share", NULL); if (!data_dirs) - goto fail; + return NULL; /* Now merge everything we found. */ if (generator_early) - if (strv_extend(&r, generator_early) < 0) - goto fail; + if (strv_extend(&res, generator_early) < 0) + return NULL; if (config_home) - if (strv_extend(&r, config_home) < 0) - goto fail; + if (strv_extend(&res, config_home) < 0) + return NULL; if (!strv_isempty(config_dirs)) - if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0) - goto fail; + if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) + return NULL; - if (strv_extend_strv(&r, (char**) config_unit_paths) < 0) - goto fail; + if (strv_extend_strv(&res, (char**) config_unit_paths) < 0) + return NULL; if (runtime_dir) - if (strv_extend(&r, runtime_dir) < 0) - goto fail; + if (strv_extend(&res, runtime_dir) < 0) + return NULL; - if (strv_extend(&r, runtime_unit_path) < 0) - goto fail; + if (strv_extend(&res, runtime_unit_path) < 0) + return NULL; if (generator) - if (strv_extend(&r, generator) < 0) - goto fail; + if (strv_extend(&res, generator) < 0) + return NULL; if (data_home) - if (strv_extend(&r, data_home) < 0) - goto fail; + if (strv_extend(&res, data_home) < 0) + return NULL; if (!strv_isempty(data_dirs)) - if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0) - goto fail; + if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) + return NULL; - if (strv_extend_strv(&r, (char**) data_unit_paths) < 0) - goto fail; + if (strv_extend_strv(&res, (char**) data_unit_paths) < 0) + return NULL; if (generator_late) - if (strv_extend(&r, generator_late) < 0) - goto fail; + if (strv_extend(&res, generator_late) < 0) + return NULL; - if (!path_strv_make_absolute_cwd(r)) - goto fail; + if (!path_strv_make_absolute_cwd(res)) + return NULL; - return r; + tmp = res; + res = NULL; + return tmp; +} -fail: - strv_free(r); - return NULL; +char **generator_paths(SystemdRunningAs running_as) { + if (running_as == SYSTEMD_USER) + return strv_new("/etc/systemd/user-generators", + "/run/systemd/user-generators", + "/usr/local/lib/systemd/user-generators", + USER_GENERATOR_PATH, + NULL); + else + return strv_new("/etc/systemd/system-generators", + "/run/systemd/system-generators", + "/usr/local/lib/systemd/system-generators", + SYSTEM_GENERATOR_PATH, + NULL); } int lookup_paths_init( diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index 655e4549ca..2ec888da81 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -42,6 +42,8 @@ typedef enum SystemdRunningAs { int user_config_home(char **config_home); int user_runtime_dir(char **runtime_dir); +char **generator_paths(SystemdRunningAs running_as); + int lookup_paths_init(LookupPaths *p, SystemdRunningAs running_as, bool personal, diff --git a/src/shared/util.c b/src/shared/util.c index 06bd1b9f04..6520e511f0 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -4037,10 +4037,10 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { return endswith(de->d_name, suffix); } -static int do_execute(const char *directory, usec_t timeout, char *argv[]) { +static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; - _cleanup_closedir_ DIR *d; - struct dirent *de; + _cleanup_set_free_free_ Set *seen = NULL; + char **directory; /* We fork this all off from a child process so that we can * somewhat cleanly make use of SIGALRM to set a time limit */ @@ -4050,57 +4050,80 @@ static int do_execute(const char *directory, usec_t timeout, char *argv[]) { assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - d = opendir(directory); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open directory %s: %m", directory); - } - pids = hashmap_new(NULL); if (!pids) return log_oom(); - FOREACH_DIRENT(de, d, break) { - _cleanup_free_ char *path = NULL; - pid_t pid; - int r; + seen = set_new(&string_hash_ops); + if (!seen) + return log_oom(); - if (!dirent_is_file(de)) - continue; + STRV_FOREACH(directory, directories) { + _cleanup_closedir_ DIR *d; + struct dirent *de; - path = strjoin(directory, "/", de->d_name, NULL); - if (!path) - return log_oom(); + d = opendir(*directory); + if (!d) { + if (errno == ENOENT) + continue; - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - continue; - } else if (pid == 0) { - char *_argv[2]; + return log_error_errno(errno, "Failed to open directory %s: %m", *directory); + } - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + FOREACH_DIRENT(de, d, break) { + _cleanup_free_ char *path = NULL; + pid_t pid; + int r; - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; + if (!dirent_is_file(de)) + continue; + + if (set_contains(seen, de->d_name)) { + log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); + continue; + } + + r = set_put_strdup(seen, de->d_name); + if (r < 0) + return log_oom(); + + path = strjoin(*directory, "/", de->d_name, NULL); + if (!path) + return log_oom(); + + if (null_or_empty_path(path)) { + log_debug("%s is empty (a mask).", path); + continue; } else - argv[0] = path; + log_debug("%s will be executed.", path); - execv(path, argv); - return log_error_errno(errno, "Failed to execute %s: %m", path); - } + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + continue; + } else if (pid == 0) { + char *_argv[2]; - log_debug("Spawned %s as " PID_FMT ".", path, pid); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - r = hashmap_put(pids, UINT_TO_PTR(pid), path); - if (r < 0) - return log_oom(); + if (!argv) { + _argv[0] = path; + _argv[1] = NULL; + argv = _argv; + } else + argv[0] = path; + + execv(path, argv); + return log_error_errno(errno, "Failed to execute %s: %m", path); + } + + log_debug("Spawned %s as " PID_FMT ".", path, pid); - path = NULL; + r = hashmap_put(pids, UINT_TO_PTR(pid), path); + if (r < 0) + return log_oom(); + path = NULL; + } } /* Abort execution of this process after the timout. We simply @@ -4126,14 +4149,21 @@ static int do_execute(const char *directory, usec_t timeout, char *argv[]) { return 0; } -void execute_directory(const char *directory, usec_t timeout, char *argv[]) { +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { pid_t executor_pid; int r; + char *name; + char **dirs = (char**) directories; + + assert(!strv_isempty(dirs)); - assert(directory); + name = basename(dirs[0]); + assert(!isempty(name)); - /* Executes all binaries in the directory in parallel and waits - * for them to finish. Optionally a timeout is applied. */ + /* Executes all binaries in the directories in parallel and waits + * for them to finish. Optionally a timeout is applied. If a file + * with the same name exists in more than one directory, the + * earliest one wins. */ executor_pid = fork(); if (executor_pid < 0) { @@ -4141,11 +4171,11 @@ void execute_directory(const char *directory, usec_t timeout, char *argv[]) { return; } else if (executor_pid == 0) { - r = do_execute(directory, timeout, argv); + r = do_execute(dirs, timeout, argv); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } - wait_for_terminate_and_warn(directory, executor_pid, true); + wait_for_terminate_and_warn(name, executor_pid, true); } int kill_and_sigcont(pid_t pid, int sig) { diff --git a/src/shared/util.h b/src/shared/util.h index 3d31cb3f04..141a3feab6 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -534,7 +534,7 @@ bool tty_is_console(const char *tty) _pure_; int vtnr_from_tty(const char *tty); const char *default_term_for_tty(const char *tty); -void execute_directory(const char *directory, usec_t timeout, char *argv[]); +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); int kill_and_sigcont(pid_t pid, int sig); diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 2fb5f0c8e0..cc1ffa63f1 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -92,6 +92,7 @@ static int execute(char **modes, char **states) { arg_verb, NULL }; + static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; int r; _cleanup_fclose_ FILE *f = NULL; @@ -107,7 +108,7 @@ static int execute(char **modes, char **states) { if (r < 0) return r; - execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); log_struct(LOG_INFO, LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), @@ -126,7 +127,7 @@ static int execute(char **modes, char **states) { NULL); arguments[1] = (char*) "post"; - execute_directory(SYSTEM_SLEEP_PATH, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); return r; } diff --git a/src/test/test-util.c b/src/test/test-util.c index d862149fa8..1a5997873c 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -1207,23 +1207,28 @@ static void test_glob_exists(void) { } static void test_execute_directory(void) { - char name[] = "/tmp/test-execute_directory/script1"; - char name2[] = "/tmp/test-execute_directory/script2"; - char name3[] = "/tmp/test-execute_directory/useless"; - char tempdir[] = "/tmp/test-execute_directory/"; + char template[] = "/tmp/test-readlink_and_make_absolute.XXXXXXX"; + const char const* dirs[] = {template, NULL}; + const char *name, *name2, *name3; - assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid()) >= 0); - assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch /tmp/test-execute_directory/it_works") == 0); - assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch /tmp/test-execute_directory/it_works2") == 0); + assert_se(mkdtemp(template)); + + name = strappenda(template, "/script"); + name2 = strappenda(template, "/script2"); + name3 = strappenda(template, "/useless"); + + assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works") == 0); + assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2") == 0); assert_se(chmod(name, 0755) == 0); assert_se(chmod(name2, 0755) == 0); assert_se(touch(name3) >= 0); - execute_directory(tempdir, DEFAULT_TIMEOUT_USEC, NULL); - assert_se(access("/tmp/test-execute_directory/it_works", F_OK) >= 0); - assert_se(access("/tmp/test-execute_directory/it_works2", F_OK) >= 0); + assert(chdir(template) == 0); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL); + assert_se(access("it_works", F_OK) >= 0); + assert_se(access("it_works2", F_OK) >= 0); - rm_rf_dangerous(tempdir, false, true, false); + rm_rf_dangerous(template, false, true, false); } static void test_unquote_first_word(void) { |