From: Nandakumar Raghavan Date: Thu, 16 Jul 2026 16:08:27 +0000 (+0000) Subject: networkctl,networkd: add --no-reconfigure flag to networkctl reload X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=13aec9fddffbf52e50c67ab7bc6575abb3a1588f;p=thirdparty%2Fsystemd.git networkctl,networkd: add --no-reconfigure flag to networkctl reload Add a new --no-reconfigure flag to 'networkctl reload' that reloads .network and .netdev files from disk without reconfiguring any network interfaces. This may be useful to avoid reconfiguring multiple interfaces simultaneously when multiple .network files are updated, or when an updated .network file is applied to multiple interfaces. On the networkd side, manager_reload() gains a reconfigure_links parameter that gates the per-link reconfiguration loop. A new io.systemd.Network.Reload varlink method is added that exposes this as an optional reconfigureLinks boolean (defaults to true). Both plain 'networkctl reload' and '--no-reconfigure' now unconditionally call this method first. If an older networkd returns MethodNotFound, plain reload falls back to io.systemd.service.Reload for backward compatibility; '--no-reconfigure' fails with a clear error in that case. --- diff --git a/man/networkctl.xml b/man/networkctl.xml index b436797999e..4905c78506b 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -543,7 +543,10 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) If a new, modified, or removed .network file is found, then all - interfaces that matched the file are reconfigured. + interfaces that matched the file are reconfigured. This reconfiguration step can be skipped by + passing , in which case only config files are reloaded from disk + without reconfiguring any network interfaces. Use networkctl reconfigure afterwards + to selectively apply config changes per interface. @@ -729,6 +732,21 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) + + + + + When used with reload, only reload config files from disk without + reconfiguring any network interfaces. This may be useful to avoid reconfiguring multiple + interfaces simultaneously when multiple .network files are updated, + or when an updated .network file is applied to multiple interfaces. + Use networkctl reconfigure afterwards to apply changes selectively + per interface. + + + + + diff --git a/shell-completion/bash/networkctl b/shell-completion/bash/networkctl index fe186a49a2e..ddb3b662d27 100644 --- a/shell-completion/bash/networkctl +++ b/shell-completion/bash/networkctl @@ -45,7 +45,7 @@ _networkctl() { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword local -A OPTS=( [STANDALONE]='-a --all -h --help --version --no-pager --no-legend --no-ask-password - -s --stats -l --full --no-reload --runtime' + -s --stats -l --full --no-reload --no-reconfigure --runtime' [ARG]='-n --lines --json --drop-in' ) diff --git a/shell-completion/zsh/_networkctl b/shell-completion/zsh/_networkctl index c44b3469498..4c54cde486d 100644 --- a/shell-completion/zsh/_networkctl +++ b/shell-completion/zsh/_networkctl @@ -58,5 +58,6 @@ _arguments \ '(- *)--version[Show package version]' \ '--drop-in=[Use the given drop-in file name]:NAME' \ '--no-reload[Do not reload the network manager state when editing]' \ + '--no-reconfigure[Reload config files without reconfiguring network interfaces]' \ '--json=[Shows output formatted as JSON]:format:_networkctl_get_json' \ '*::networkctl commands:_networkctl_commands' diff --git a/src/network/networkctl-config-file.c b/src/network/networkctl-config-file.c index f69c509023a..1b5cf1718ff 100644 --- a/src/network/networkctl-config-file.c +++ b/src/network/networkctl-config-file.c @@ -411,7 +411,7 @@ static int reload_daemons(ReloadFlags flags) { if (!networkd_is_running()) log_debug("systemd-networkd is not running, skipping reload."); else - RET_GATHER(ret, reload_networkd()); + RET_GATHER(ret, reload_networkd(/* reconfigure_links= */ true)); } return ret; diff --git a/src/network/networkctl-misc.c b/src/network/networkctl-misc.c index 0436346bc86..2115da16b60 100644 --- a/src/network/networkctl-misc.c +++ b/src/network/networkctl-misc.c @@ -123,7 +123,7 @@ int verb_link_varlink_simple_method(int argc, char *argv[], uintptr_t _data, voi } int verb_reload(int argc, char *argv[], uintptr_t _data, void *userdata) { - return reload_networkd(); + return reload_networkd(!arg_no_reconfigure); } int verb_persistent_storage(int argc, char *argv[], uintptr_t _data, void *userdata) { diff --git a/src/network/networkctl-util.c b/src/network/networkctl-util.c index 590c8a6abc2..22710d76316 100644 --- a/src/network/networkctl-util.c +++ b/src/network/networkctl-util.c @@ -60,7 +60,7 @@ int varlink_connect_networkd(sd_varlink **ret_varlink) { return 0; } -int reload_networkd(void) { +int reload_networkd(bool reconfigure_links) { _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL; int r; @@ -70,11 +70,39 @@ int reload_networkd(void) { (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password); - return varlink_callbo_and_log( + sd_json_variant *reply = NULL; + const char *error_id = NULL; + r = sd_varlink_callbo( vl, - "io.systemd.service.Reload", - /* reply= */ NULL, + "io.systemd.Network.Reload", + &reply, + &error_id, + SD_JSON_BUILD_PAIR_BOOLEAN("reconfigureLinks", reconfigure_links), SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password)); + if (r < 0) + return log_error_errno(r, "Failed to call io.systemd.Network.Reload varlink method: %m"); + + if (error_id) { + if (streq(error_id, SD_VARLINK_ERROR_METHOD_NOT_FOUND)) { + if (!reconfigure_links) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Installed version of systemd-networkd does not support --no-reconfigure."); + + /* Older networkd without io.systemd.Network.Reload, fall back to io.systemd.service.Reload. */ + return varlink_callbo_and_log( + vl, + "io.systemd.service.Reload", + /* reply= */ NULL, + SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password)); + } + + r = sd_varlink_error_to_errno(error_id, reply); + if (r != -EBADR) + return log_error_errno(r, "Failed to call io.systemd.Network.Reload varlink method: %m"); + return log_error_errno(r, "Failed to call io.systemd.Network.Reload varlink method: %s", error_id); + } + + return 0; } int reload_udevd(void) { diff --git a/src/network/networkctl-util.h b/src/network/networkctl-util.h index ae0f565007e..cb5832b80f7 100644 --- a/src/network/networkctl-util.h +++ b/src/network/networkctl-util.h @@ -4,7 +4,7 @@ #include "forward.h" int varlink_connect_networkd(sd_varlink **ret_varlink); -int reload_networkd(void); +int reload_networkd(bool reconfigure_links); int reload_udevd(void); bool networkd_is_running(void); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index f7a6e6eef76..73757cc8f27 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -28,6 +28,7 @@ PagerFlags arg_pager_flags = 0; bool arg_legend = true; bool arg_no_reload = false; +bool arg_no_reconfigure = false; bool arg_all = false; bool arg_stats = false; bool arg_full = false; @@ -167,6 +168,11 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { arg_no_reload = true; break; + OPTION_LONG("no-reconfigure", NULL, + "Only reload config files, do not reconfigure network interfaces"): + arg_no_reconfigure = true; + break; + OPTION_LONG("drop-in", "NAME", "Edit specified drop-in instead of main config file"): if (isempty(opts.arg)) diff --git a/src/network/networkctl.h b/src/network/networkctl.h index 39c81e79470..2de93bd605f 100644 --- a/src/network/networkctl.h +++ b/src/network/networkctl.h @@ -7,6 +7,7 @@ extern PagerFlags arg_pager_flags; extern bool arg_legend; extern bool arg_no_reload; +extern bool arg_no_reconfigure; extern bool arg_all; extern bool arg_stats; extern bool arg_full; diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index c335d34ff1c..1fad86dc4ab 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -215,7 +215,7 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err if (r == 0) return 1; /* Polkit will call us back */ - r = manager_reload(manager, message, /* varlink= */ NULL); + r = manager_reload(manager, message, /* varlink= */ NULL, /* reconfigure_links= */ true); if (r < 0) return r; diff --git a/src/network/networkd-manager-varlink.c b/src/network/networkd-manager-varlink.c index d12847b089f..19f7b4813d0 100644 --- a/src/network/networkd-manager-varlink.c +++ b/src/network/networkd-manager-varlink.c @@ -18,6 +18,7 @@ #include "networkd-manager.h" #include "networkd-manager-varlink.h" #include "stat-util.h" +#include "string-util.h" #include "varlink-io.systemd.Network.h" #include "varlink-io.systemd.Network.Link.h" #include "varlink-io.systemd.service.h" @@ -237,6 +238,12 @@ static int vl_method_set_persistent_storage(sd_varlink *vlink, sd_json_variant * } static int vl_method_reload(sd_varlink *vlink, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + static const sd_json_dispatch_field dispatch_table[] = { + { "reconfigureLinks", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, 0, SD_JSON_NULLABLE }, + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + Manager *m = ASSERT_PTR(userdata); int r; @@ -245,7 +252,18 @@ static int vl_method_reload(sd_varlink *vlink, sd_json_variant *parameters, sd_v if (m->reloading > 0) return sd_varlink_error(vlink, "io.systemd.Network.AlreadyReloading", NULL); - r = sd_varlink_dispatch(vlink, parameters, dispatch_table_polkit_only, /* userdata= */ NULL); + const char *method; + r = sd_varlink_get_current_method(vlink, &method); + if (r < 0) + return r; + + bool is_network_reload = streq(method, "io.systemd.Network.Reload"); + int reconfigure_links = -1; /* tristate: -1 = unset (defaults to true) */ + r = sd_varlink_dispatch( + vlink, + parameters, + is_network_reload ? dispatch_table : dispatch_table_polkit_only, + &reconfigure_links); if (r != 0) return r; @@ -258,7 +276,7 @@ static int vl_method_reload(sd_varlink *vlink, sd_json_variant *parameters, sd_v if (r <= 0) return r; - r = manager_reload(m, /* message= */ NULL, vlink); + r = manager_reload(m, /* message= */ NULL, vlink, /* reconfigure_links= */ reconfigure_links != 0); if (r < 0) return log_error_errno(r, "Failed to reload: %m"); @@ -302,6 +320,7 @@ int manager_varlink_init(Manager *m, int fd) { "io.systemd.Network.GetStates", vl_method_get_states, "io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id, "io.systemd.Network.GetLLDPNeighbors", vl_method_get_lldp_neighbors, + "io.systemd.Network.Reload", vl_method_reload, "io.systemd.Network.SetPersistentStorage", vl_method_set_persistent_storage, "io.systemd.Network.Link.Describe", vl_method_link_describe, "io.systemd.Network.Link.Up", vl_method_link_up, diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 63ba0c166fa..64e022a674f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -535,7 +535,7 @@ static int signal_restart_callback(sd_event_source *s, const struct signalfd_sig static int signal_reload_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { Manager *m = ASSERT_PTR(userdata); - (void) manager_reload(m, /* message= */ NULL, /* varlink= */ NULL); + (void) manager_reload(m, /* message= */ NULL, /* varlink= */ NULL, /* reconfigure_links= */ true); return 0; } @@ -1266,7 +1266,7 @@ int manager_set_timezone(Manager *m, const char *tz) { return 0; } -int manager_reload(Manager *m, sd_bus_message *message, sd_varlink *varlink) { +int manager_reload(Manager *m, sd_bus_message *message, sd_varlink *varlink, bool reconfigure_links) { Link *link; int r; @@ -1288,13 +1288,14 @@ int manager_reload(Manager *m, sd_bus_message *message, sd_varlink *varlink) { goto finish; } - HASHMAP_FOREACH(link, m->links_by_index) - (void) link_reconfigure_full( - link, - /* flags= */ 0, - message, - varlink, - /* counter= */ (message || varlink) ? &m->reloading : NULL); + if (reconfigure_links) + HASHMAP_FOREACH(link, m->links_by_index) + (void) link_reconfigure_full( + link, + /* flags= */ 0, + message, + varlink, + /* counter= */ (message || varlink) ? &m->reloading : NULL); log_debug("Reloaded."); r = 0; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 2e1b6430907..7913e972ecb 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -159,7 +159,7 @@ int manager_enumerate(Manager *m); int manager_set_hostname(Manager *m, const char *hostname); int manager_set_timezone(Manager *m, const char *tz); -int manager_reload(Manager *m, sd_bus_message *message, sd_varlink *varlink); +int manager_reload(Manager *m, sd_bus_message *message, sd_varlink *varlink, bool reconfigure_links); static inline Hashmap** manager_get_sysctl_shadow(Manager *manager) { #if ENABLE_SYSCTL_BPF diff --git a/src/shared/varlink-io.systemd.Network.c b/src/shared/varlink-io.systemd.Network.c index e98d517cc7e..29733287774 100644 --- a/src/shared/varlink-io.systemd.Network.c +++ b/src/shared/varlink-io.systemd.Network.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "bus-polkit.h" #include "varlink-io.systemd.Network.h" /* Helper macro to define address fields with both binary and string representation */ @@ -544,6 +545,12 @@ SD_VARLINK_DEFINE_STRUCT_TYPE( SD_VARLINK_FIELD_COMMENT("Current transmit/receive bitrates from speed meter"), SD_VARLINK_DEFINE_FIELD_BY_TYPE(BitRates, BitRates, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_METHOD( + Reload, + SD_VARLINK_FIELD_COMMENT("If true (default), reload config files and reconfigure all matching network interfaces. If false, only reload config files without reconfiguring interfaces; use 'networkctl reconfigure' afterwards to apply changes selectively."), + SD_VARLINK_DEFINE_INPUT(reconfigureLinks, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), + VARLINK_DEFINE_POLKIT_INPUT); + static SD_VARLINK_DEFINE_METHOD( Describe, SD_VARLINK_FIELD_COMMENT("All network interfaces managed by systemd-networkd"), @@ -612,6 +619,7 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_GetStates, &vl_method_GetNamespaceId, &vl_method_GetLLDPNeighbors, + &vl_method_Reload, &vl_method_SetPersistentStorage, &vl_type_Address, &vl_type_BitRates, diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index ce0d5a8a150..e55a5093678 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1699,6 +1699,35 @@ class NetworkctlTests(unittest.TestCase, Utilities): networkctl_reload() self.wait_operstate('test1', 'degraded') + def test_reload_no_reconfigure(self): + copy_network_unit('12-dummy.netdev', '25-address-static.network', copy_dropins=False) + start_networkd() + self.wait_online('dummy98:routable') + + output = check_output('ip -4 address show dev dummy98') + print(output) + self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output) + + with open( + os.path.join(network_unit_dir, '25-address-static.network'), mode='w', encoding='utf-8' + ) as f: + f.write('[Match]\nName=dummy98\n\n[Network]\nIPv6AcceptRA=no\nAddress=10.99.0.1/24\n') + + networkctl('reload', '--no-reconfigure') + + output = check_output('ip -4 address show dev dummy98') + print(output) + self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output) + self.assertNotIn('inet 10.99.0.1/24', output) + + networkctl_reconfigure('dummy98') + self.wait_online('dummy98:routable') + + output = check_output('ip -4 address show dev dummy98') + print(output) + self.assertIn('inet 10.99.0.1/24 brd 10.99.0.255 scope global dummy98', output) + self.assertNotIn('inet 10.1.2.3/16', output) + def test_glob(self): copy_network_unit('11-dummy.netdev', '11-dummy.network') start_networkd()