]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
manager: refuse reloading/reexecing when /run is overly full
authorLennart Poettering <lennart@poettering.net>
Fri, 3 Feb 2017 11:12:54 +0000 (12:12 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 6 Feb 2017 15:58:06 +0000 (16:58 +0100)
Let's add an extra safety check: before entering a reload/reexec, let's
verify that there's enough room in /run for it.

Fixes: #5016
src/core/dbus-manager.c
src/core/dbus-manager.h
src/core/manager.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h

index 987625143897e82c8fc2a0545816d99f782c0a6c..0136d38833fb1fb18f897123feebb47e3785538c 100644 (file)
@@ -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"
 #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;
index 36a2e9481b11b924219579b3158a6e2fc59a69ab..9f3222da28f52e158123e5947b80c74483763c72 100644 (file)
@@ -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);
index d83c5ef5e219955eb711ea7227583698e228fb27..b22f85fee39a2393483cc3f48753e47825c58c5c 100644 (file)
@@ -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: {
index c9fd79e3b426a93a2c8647da6efe4c49f4d1d018..b40ba2520c3de8972cf02f747764e900eee08316 100644 (file)
@@ -47,6 +47,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING,            EHOSTDOWN),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER,         ESRCH),
         SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED,               EUNATCH),
+        SD_BUS_ERROR_MAP(BUS_ERROR_DISK_FULL,                    ENOSPC),
 
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE,              ENXIO),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE,                ENOENT),
index 525b79fa77ffe040d181e891582a886af7d62d7d..4523be05cec9142389c1066ac3d08816874bf46e 100644 (file)
@@ -43,6 +43,7 @@
 #define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
 #define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
 #define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
+#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
 
 #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
 #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"