Merge pull request #31811 from yuwata/network-pin-persistent-storage

network: pin file descriptor of persistent storage
This commit is contained in:
Luca Boccassi 2024-03-18 11:08:21 +00:00 committed by GitHub
commit 48570c9273
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 87 additions and 51 deletions

View file

@ -618,6 +618,13 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
`nftables`. Selects the firewall backend to use. If not specified tries to
use `nftables` and falls back to `iptables` if that's not available.
`systemd-networkd`:
* `$SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY` takes a boolean. If true,
systemd-networkd tries to open the persistent storage on start. To make this
work, ProtectSystem=strict in systemd-networkd.service needs to be downgraded
or disabled.
`systemd-storagetm`:
* `$SYSTEMD_NVME_MODEL`, `$SYSTEMD_NVME_FIRMWARE`, `$SYSTEMD_NVME_SERIAL`,

View file

@ -156,12 +156,25 @@ int link_start_dhcp4_server(Link *link) {
if (!link_has_carrier(link))
return 0;
if (sd_dhcp_server_is_running(link->dhcp_server))
return 0; /* already started. */
/* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then
* the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly
* handled as expired and dropped. */
if (!sd_dhcp_server_is_in_relay_mode(link->dhcp_server) &&
!link->manager->persistent_storage_is_ready)
return 0;
if (!sd_dhcp_server_is_in_relay_mode(link->dhcp_server)) {
if (link->manager->persistent_storage_fd < 0)
return 0; /* persistent storage is not ready. */
_cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname);
if (!lease_file)
return -ENOMEM;
r = sd_dhcp_server_set_lease_file(link->dhcp_server, link->manager->persistent_storage_fd, lease_file);
if (r < 0)
return r;
}
r = sd_dhcp_server_start(link->dhcp_server);
if (r < 0)
@ -183,13 +196,18 @@ void manager_toggle_dhcp4_server_state(Manager *manager, bool start) {
if (sd_dhcp_server_is_in_relay_mode(link->dhcp_server))
continue;
if (start)
r = link_start_dhcp4_server(link);
else
r = sd_dhcp_server_stop(link->dhcp_server);
/* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set
* the lease file in link_start_dhcp4_server(). */
r = sd_dhcp_server_stop(link->dhcp_server);
if (r < 0)
log_link_debug_errno(link, r, "Failed to %s DHCP server, ignoring: %m",
start ? "start" : "stop");
log_link_debug_errno(link, r, "Failed to stop DHCP server, ignoring: %m");
if (!start)
continue;
r = link_start_dhcp4_server(link);
if (r < 0)
log_link_debug_errno(link, r, "Failed to start DHCP server, ignoring: %m");
}
}
@ -572,16 +590,6 @@ static int dhcp4_server_configure(Link *link) {
return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
}
if (!sd_dhcp_server_is_in_relay_mode(link->dhcp_server)) {
_cleanup_free_ char *lease_file = path_join("/var/lib/systemd/network/dhcp-server-lease/", link->ifname);
if (!lease_file)
return log_oom();
r = sd_dhcp_server_set_lease_file(link->dhcp_server, AT_FDCWD, lease_file);
if (r < 0)
log_link_warning_errno(link, r, "Failed to load DHCPv4 server leases, ignoring: %m");
}
r = link_start_dhcp4_server(link);
if (r < 0)
return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");

View file

@ -3,7 +3,7 @@
#include <unistd.h>
#include "bus-polkit.h"
#include "fs-util.h"
#include "fd-util.h"
#include "lldp-rx-internal.h"
#include "networkd-dhcp-server.h"
#include "networkd-manager-varlink.h"
@ -185,13 +185,32 @@ static int vl_method_set_persistent_storage(Varlink *vlink, JsonVariant *paramet
return r;
if (ready) {
r = path_is_read_only_fs("/var/lib/systemd/network/");
_cleanup_close_ int fd = -EBADF;
struct stat st, st_prev;
fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY | O_PATH);
if (fd < 0)
return log_warning_errno(errno, "Failed to open /var/lib/systemd/network/: %m");
r = fd_is_read_only_fs(fd);
if (r < 0)
return log_warning_errno(r, "Failed to check if /var/lib/systemd/network/ is writable: %m");
return log_warning_errno(r, "Failed to check if the persistent storage is writable: %m");
if (r > 0) {
log_warning("The directory /var/lib/systemd/network/ is read-only.");
log_warning("The persistent storage is on read-only filesystem.");
return varlink_error(vlink, "io.systemd.Network.StorageReadOnly", NULL);
}
if (fstat(fd, &st) < 0)
return log_warning_errno(r, "Failed to stat the persistent storage: %m");
if (manager->persistent_storage_fd >= 0 &&
fstat(manager->persistent_storage_fd, &st_prev) >= 0 &&
stat_inode_same(&st, &st_prev))
return varlink_reply(vlink, NULL);
} else {
if (manager->persistent_storage_fd < 0)
return varlink_reply(vlink, NULL);
}
r = varlink_verify_polkit_async(
@ -203,16 +222,16 @@ static int vl_method_set_persistent_storage(Varlink *vlink, JsonVariant *paramet
if (r <= 0)
return r;
manager->persistent_storage_is_ready = ready;
if (ready) {
r = touch("/run/systemd/netif/persistent-storage-ready");
if (r < 0)
log_debug_errno(r, "Failed to create /run/systemd/netif/persistent-storage-ready, ignoring: %m");
} else {
if (unlink("/run/systemd/netif/persistent-storage-ready") < 0 && errno != ENOENT)
log_debug_errno(errno, "Failed to remove /run/systemd/netif/persistent-storage-ready, ignoring: %m");
}
_cleanup_close_ int fd = -EBADF;
fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY | O_PATH);
if (fd < 0)
return log_warning_errno(errno, "Failed to open /var/lib/systemd/network/: %m");
close_and_replace(manager->persistent_storage_fd, fd);
} else
manager->persistent_storage_fd = safe_close(manager->persistent_storage_fd);
manager_toggle_dhcp4_server_state(manager, ready);

View file

@ -23,6 +23,7 @@
#include "device-private.h"
#include "device-util.h"
#include "dns-domain.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "firewall-util.h"
@ -557,27 +558,27 @@ int manager_setup(Manager *m) {
return 0;
}
static bool persistent_storage_is_ready(void) {
static int persistent_storage_open(void) {
_cleanup_close_ int fd = -EBADF;
int r;
if (access("/run/systemd/netif/persistent-storage-ready", F_OK) < 0) {
if (errno != ENOENT)
log_debug_errno(errno, "Failed to check if /run/systemd/netif/persistent-storage-ready exists, assuming not: %m");
return false;
}
r = getenv_bool("SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY");
if (r < 0 && r != -ENXIO)
return log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY environment variable, ignoring: %m");
if (r <= 0)
return -EBADF;
r = path_is_read_only_fs("/var/lib/systemd/network/");
if (r == 0)
return true;
fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY | O_PATH);
if (fd < 0)
return log_debug_errno(errno, "Failed to open /var/lib/systemd/network/, ignoring: %m");
r = fd_is_read_only_fs(fd);
if (r < 0)
log_debug_errno(r, "Failed to check if /var/lib/systemd/network/ is writable: %m");
else
log_debug("The directory /var/lib/systemd/network/ is read-only.");
return log_debug_errno(r, "Failed to check if /var/lib/systemd/network/ is writable: %m");
if (r > 0)
return log_debug_errno(SYNTHETIC_ERRNO(EROFS), "The directory /var/lib/systemd/network/ is on read-only filesystem.");
if (unlink("/run/systemd/netif/persistent-storage-ready") < 0 && errno != ENOENT)
log_debug_errno(errno, "Failed to remove /run/systemd/netif/persistent-storage-ready, ignoring: %m");
return false;
return TAKE_FD(fd);
}
int manager_new(Manager **ret, bool test_mode) {
@ -591,13 +592,13 @@ int manager_new(Manager **ret, bool test_mode) {
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
.test_mode = test_mode,
.persistent_storage_is_ready = persistent_storage_is_ready(),
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
.online_state = _LINK_ONLINE_STATE_INVALID,
.manage_foreign_routes = true,
.manage_foreign_rules = true,
.manage_foreign_nexthops = true,
.ethtool_fd = -EBADF,
.persistent_storage_fd = persistent_storage_open(),
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
@ -672,6 +673,7 @@ Manager* manager_free(Manager *m) {
free(m->dynamic_hostname);
safe_close(m->ethtool_fd);
safe_close(m->persistent_storage_fd);
m->fw_ctx = fw_ctx_free(m->fw_ctx);

View file

@ -30,6 +30,7 @@ struct Manager {
sd_device_monitor *device_monitor;
Hashmap *polkit_registry;
int ethtool_fd;
int persistent_storage_fd;
KeepConfiguration keep_configuration;
IPv6PrivacyExtensions ipv6_privacy_extensions;
@ -41,7 +42,6 @@ struct Manager {
bool manage_foreign_routes;
bool manage_foreign_rules;
bool manage_foreign_nexthops;
bool persistent_storage_is_ready;
Set *dirty_links;
Set *new_wlan_ifindices;

View file

@ -921,7 +921,7 @@ EOF
# For the networkd instance invoked below cannot support varlink connection.
# Hence, 'networkctl persistent-storage yes' cannot be used.
touch /run/systemd/netif/persistent-storage-ready
export SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY=1
# run networkd as in systemd-networkd.service
exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=//; s/^[@+-]//; s/^!*//; p}}')