diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/dbus-manager.c | 63 | ||||
-rw-r--r-- | src/core/dbus-manager.h | 2 | ||||
-rw-r--r-- | src/core/load-dropin.c | 116 | ||||
-rw-r--r-- | src/core/load-dropin.h | 10 | ||||
-rw-r--r-- | src/core/manager.c | 24 |
5 files changed, 178 insertions, 37 deletions
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9876251438..0136d38833 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -19,6 +19,7 @@ #include <errno.h> #include <sys/prctl.h> +#include <sys/statvfs.h> #include <unistd.h> #include "alloc-util.h" @@ -38,6 +39,7 @@ #include "fs-util.h" #include "install.h" #include "log.h" +#include "parse-util.h" #include "path-util.h" #include "selinux-access.h" #include "stat-util.h" @@ -48,6 +50,10 @@ #include "virt.h" #include "watchdog.h" +/* Require 16MiB free in /run/systemd for reloading/reexecing. After all we need to serialize our state there, and if + * we can't we'll fail badly. */ +#define RELOAD_DISK_SPACE_MIN (UINT64_C(16) * UINT64_C(1024) * UINT64_C(1024)) + static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) { return (runtime ? UNIT_FILE_RUNTIME : 0) | (force ? UNIT_FILE_FORCE : 0); @@ -1312,6 +1318,40 @@ static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bu return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed."); } +static int verify_run_space(const char *message, sd_bus_error *error) { + struct statvfs svfs; + uint64_t available; + + if (statvfs("/run/systemd", &svfs) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m"); + + available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize; + + if (available < RELOAD_DISK_SPACE_MIN) { + char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX]; + return sd_bus_error_setf(error, + BUS_ERROR_DISK_FULL, + "%s, not enough space available on /run/systemd. " + "Currently, %s are free, but a safety buffer of %s is enforced.", + message, + format_bytes(fb_available, sizeof(fb_available), available), + format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN)); + } + + return 0; +} + +int verify_run_space_and_log(const char *message) { + sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = verify_run_space(message, &error); + if (r < 0) + log_error_errno(r, "%s", bus_error_message(&error, r)); + + return r; +} + static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; int r; @@ -1319,6 +1359,10 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error * assert(message); assert(m); + r = verify_run_space("Refusing to reload", error); + if (r < 0) + return r; + r = mac_selinux_access_check(message, "reload", error); if (r < 0) return r; @@ -1351,6 +1395,10 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); assert(m); + r = verify_run_space("Refusing to reexecute", error); + if (r < 0) + return r; + r = mac_selinux_access_check(message, "reload", error); if (r < 0) return r; @@ -1469,11 +1517,26 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er char *ri = NULL, *rt = NULL; const char *root, *init; Manager *m = userdata; + struct statvfs svfs; + uint64_t available; int r; assert(message); assert(m); + if (statvfs("/run/systemd", &svfs) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m"); + + available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize; + + if (available < RELOAD_DISK_SPACE_MIN) { + char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX]; + log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfuly. " + "Currently, %s are free, but %s are suggested. Proceeding anyway.", + format_bytes(fb_available, sizeof(fb_available), available), + format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN)); + } + r = mac_selinux_access_check(message, "reboot", error); if (r < 0) return r; diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h index 36a2e9481b..9f3222da28 100644 --- a/src/core/dbus-manager.h +++ b/src/core/dbus-manager.h @@ -26,3 +26,5 @@ extern const sd_bus_vtable bus_manager_vtable[]; void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); void bus_manager_send_reloading(Manager *m, bool active); void bus_manager_send_change_signal(Manager *m); + +int verify_run_space_and_log(const char *message); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index fc07151d37..ff3636149a 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -19,53 +19,121 @@ #include "conf-parser.h" +#include "fs-util.h" #include "load-dropin.h" #include "load-fragment.h" #include "log.h" +#include "stat-util.h" +#include "string-util.h" #include "strv.h" #include "unit-name.h" #include "unit.h" -static int add_dependency_consumer( - UnitDependency dependency, - const char *entry, - const char* filepath, - void *arg) { - Unit *u = arg; +static bool unit_name_compatible(const char *a, const char *b) { + _cleanup_free_ char *prefix = NULL; int r; - assert(u); + /* the straightforward case: the symlink name matches the target */ + if (streq(a, b)) + return true; + + r = unit_name_template(a, &prefix); + if (r < 0) { + log_oom(); + return true; + } + + /* an instance name points to a target that is just the template name */ + if (streq(prefix, b)) + return true; + + return false; +} + +static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { + _cleanup_strv_free_ char **paths = NULL; + char **p; + int r; - r = unit_add_dependency_by_name(u, dependency, entry, filepath, true); + r = unit_file_find_dropin_paths(NULL, + u->manager->lookup_paths.search_path, + u->manager->unit_path_cache, + dir_suffix, + NULL, + u->names, + &paths); if (r < 0) - log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", entry, u->id); + return r; + + STRV_FOREACH(p, paths) { + const char *entry; + _cleanup_free_ char *target = NULL; + + entry = basename(*p); + + if (null_or_empty_path(*p) > 0) { + /* an error usually means an invalid symlink, which is not a mask */ + log_unit_debug(u, "%s dependency on %s is masked by %s, ignoring.", + unit_dependency_to_string(dependency), entry, *p); + continue; + } + + r = is_symlink(*p); + if (r < 0) { + log_unit_warning_errno(u, r, "%s dropin %s unreadable, ignoring: %m", + unit_dependency_to_string(dependency), *p); + continue; + } + if (r == 0) { + log_unit_warning(u, "%s dependency dropin %s is not a symlink, ignoring.", + unit_dependency_to_string(dependency), *p); + continue; + } + + if (!unit_name_is_valid(entry, UNIT_NAME_ANY)) { + log_unit_warning(u, "%s dependency dropin %s is not a valid unit name, ignoring.", + unit_dependency_to_string(dependency), *p); + continue; + } + + r = readlink_malloc(*p, &target); + if (r < 0) { + log_unit_warning_errno(u, r, "readlink(\"%s\") failed, ignoring: %m", *p); + continue; + } + + /* We don't treat this as an error, especially because we didn't check this for a + * long time. Nevertheless, we warn, because such mismatch can be mighty confusing. */ + if (!unit_name_compatible(entry, basename(target))) + log_unit_warning(u, "%s dependency dropin %s target %s has different name", + unit_dependency_to_string(dependency), *p, target); + + r = unit_add_dependency_by_name(u, dependency, entry, *p, true); + if (r < 0) + log_unit_error_errno(u, r, "cannot add %s dependency on %s, ignoring: %m", + unit_dependency_to_string(dependency), entry); + } return 0; } int unit_load_dropin(Unit *u) { _cleanup_strv_free_ char **l = NULL; - Iterator i; - char *t, **f; + char **f; int r; assert(u); - /* Load dependencies from supplementary drop-in directories */ - - SET_FOREACH(t, u->names, i) { - char **p; + /* Load dependencies from .wants and .requires directories */ + r = process_deps(u, UNIT_WANTS, ".wants"); + if (r < 0) + return r; - STRV_FOREACH(p, u->manager->lookup_paths.search_path) { - unit_file_process_dir(NULL, u->manager->unit_path_cache, *p, t, - ".wants", UNIT_WANTS, - add_dependency_consumer, u, NULL); - unit_file_process_dir(NULL, u->manager->unit_path_cache, *p, t, - ".requires", UNIT_REQUIRES, - add_dependency_consumer, u, NULL); - } - } + r = process_deps(u, UNIT_REQUIRES, ".requires"); + if (r < 0) + return r; + /* Load .conf dropins */ r = unit_find_dropin_paths(u, &l); if (r <= 0) return 0; diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 319827dfb9..5828a223ce 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -25,11 +25,11 @@ /* Read service data supplementary drop-in directories */ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { - return unit_file_find_dropin_paths(NULL, - u->manager->lookup_paths.search_path, - u->manager->unit_path_cache, - u->names, - paths); + return unit_file_find_dropin_conf_paths(NULL, + u->manager->lookup_paths.search_path, + u->manager->unit_path_cache, + u->names, + paths); } int unit_load_dropin(Unit *u); diff --git a/src/core/manager.c b/src/core/manager.c index d83c5ef5e2..e4da945777 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1984,7 +1984,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t if (MANAGER_IS_SYSTEM(m)) { /* This is for compatibility with the * original sysvinit */ - m->exit_code = MANAGER_REEXECUTE; + r = verify_run_space_and_log("Refusing to reexecute"); + if (r >= 0) + m->exit_code = MANAGER_REEXECUTE; break; } @@ -2061,7 +2063,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t } case SIGHUP: - m->exit_code = MANAGER_RELOAD; + r = verify_run_space_and_log("Refusing to reload"); + if (r >= 0) + m->exit_code = MANAGER_RELOAD; break; default: { @@ -2432,18 +2436,22 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } int manager_open_serialization(Manager *m, FILE **_f) { - const char *path; int fd = -1; FILE *f; assert(_f); - path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; + fd = memfd_create("systemd-serialization", MFD_CLOEXEC); + if (fd < 0) { + const char *path; - log_debug("Serializing state to %s", path); + path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; + fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); + if (fd < 0) + return -errno; + log_debug("Serializing state to %s.", path); + } else + log_debug("Serializing state to memfd."); f = fdopen(fd, "w+"); if (!f) { |