]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl,networkd: add --no-reconfigure flag to networkctl reload
authorNandakumar Raghavan <naraghavan@microsoft.com>
Thu, 16 Jul 2026 16:08:27 +0000 (16:08 +0000)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 20 Jul 2026 05:35:04 +0000 (14:35 +0900)
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.

15 files changed:
man/networkctl.xml
shell-completion/bash/networkctl
shell-completion/zsh/_networkctl
src/network/networkctl-config-file.c
src/network/networkctl-misc.c
src/network/networkctl-util.c
src/network/networkctl-util.h
src/network/networkctl.c
src/network/networkctl.h
src/network/networkd-manager-bus.c
src/network/networkd-manager-varlink.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/shared/varlink-io.systemd.Network.c
test/test-network/systemd-networkd-tests.py

index b436797999e045a4ceeb12e711516f4677ae682d..4905c78506bc06ee5acdf3bcf42efae9ac19072c 100644 (file)
@@ -543,7 +543,10 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
           </para>
 
           <para>If a new, modified, or removed <filename>.network</filename> file is found, then all
-          interfaces that matched the file are reconfigured.</para>
+          interfaces that matched the file are reconfigured. This reconfiguration step can be skipped by
+          passing <option>--no-reconfigure</option>, in which case only config files are reloaded from disk
+          without reconfiguring any network interfaces. Use <command>networkctl reconfigure</command> afterwards
+          to selectively apply config changes per interface.</para>
 
           <xi:include href="version-info.xml" xpointer="v244"/>
         </listitem>
@@ -729,6 +732,21 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--no-reconfigure</option></term>
+
+        <listitem>
+          <para>When used with <command>reload</command>, only reload config files from disk without
+          reconfiguring any network interfaces. This may be useful to avoid reconfiguring multiple
+          interfaces simultaneously when multiple <filename>.network</filename> files are updated,
+          or when an updated <filename>.network</filename> file is applied to multiple interfaces.
+          Use <command>networkctl reconfigure</command> afterwards to apply changes selectively
+          per interface.</para>
+
+          <xi:include href="version-info.xml" xpointer="v262"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--runtime</option></term>
 
index fe186a49a2e7f27d92b0aa9e665bfc11d8cf2b7f..ddb3b662d277520321fb727b298f5f8053840aa6 100644 (file)
@@ -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'
     )
 
index c44b3469498345a3ae830773d7b53f8b673d7754..4c54cde486db9b870988aa823c103d5da13e3d24 100644 (file)
@@ -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'
index f69c509023ab9cf7d2d275d17e2f56c1c2dc9806..1b5cf1718ff9c3c5e64f58e22148ee02e903dc71 100644 (file)
@@ -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;
index 0436346bc863e247f0b3c03f54e27f3ee9c47d6e..2115da16b60f7c7734235761dce90de34812fc9f 100644 (file)
@@ -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) {
index 590c8a6abc2fbc7b52bbd8710333ca83a1ed8a61..22710d76316afedf535253c3df3df43df0ba3941 100644 (file)
@@ -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) {
index ae0f565007ebfeda1aa28feb40882d3a56326804..cb5832b80f75921c610d5734fbea59d1943060b8 100644 (file)
@@ -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);
 
index f7a6e6eef7619550a6a2128edd740b9c16a5cc6a..73757cc8f2715843eaa45906b526b4e3d7739141 100644 (file)
@@ -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))
index 39c81e794705ac023db81a9323de9b92255d361b..2de93bd605fe55f28eb21d55b52e5e760621c2ee 100644 (file)
@@ -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;
index c335d34ff1c26c4b4ccae705cdc0c5f5f30fdfe4..1fad86dc4ab624e6bbb3dee8938d04535d61d478 100644 (file)
@@ -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;
 
index d12847b089f4339ea9e9abc8424fe5c0db381b57..19f7b4813d0ea574a4b0949eca8f6679fcb9e284 100644 (file)
@@ -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,
index 63ba0c166fa83166dbe8dded37668663cd85acc8..64e022a674ff6ef5961f0f5c895e58989ce3bf77 100644 (file)
@@ -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;
index 2e1b6430907ee36d6f4fdcfd02bd319a1b02f4f0..7913e972ecb31f36bb6128cd4fbf07b11d2451ff 100644 (file)
@@ -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
index e98d517cc7ec4af89d7d99b2d35d56b39eb40fc7..29733287774fc026a06ba714faad00a9bd552f5a 100644 (file)
@@ -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,
index ce0d5a8a150b01986ba919e9cf4f6867ce5795bf..e55a5093678fcf8479fedacb4f3af2c26f03a99f 100755 (executable)
@@ -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()