]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13452 from yuwata/network-reload
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 24 Oct 2019 09:07:24 +0000 (11:07 +0200)
committerGitHub <noreply@github.com>
Thu, 24 Oct 2019 09:07:24 +0000 (11:07 +0200)
network: add networkctl reload and reconfigure

17 files changed:
man/networkctl.xml
src/basic/format-util.c
src/basic/format-util.h
src/network/fuzz-network-parser.c
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/networkctl.c
src/network/networkd-link-bus.c
src/network/networkd-link-bus.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-bus.c
src/network/networkd-manager.c
src/network/networkd-network.c
src/network/networkd-network.h
src/network/org.freedesktop.network1.policy
test/test-network/systemd-networkd-tests.py

index 14277a75f01ad48c632148758bf263df3c1d23c7..0502ab752c8e32f8c5422e6a01e8e18406f06ed6 100644 (file)
@@ -261,6 +261,25 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         Takes interface name or index number.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>
+          <command>reconfigure</command>
+        </term>
+        <listitem><para>Reconfigure network interfaces. Takes interface name or index number.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <command>reload</command>
+        </term>
+        <listitem><para>Reload <filename>.netdev</filename> and <filename>.network</filename> files.
+        If a new <filename>.netdev</filename> file is found, then the corresponding netdev is created.
+        Note that even if an existing <filename>.netdev</filename> is modified or removed,
+        <command>systemd-networkd</command> does not update or remove the netdev.
+        If a new, modified or removed <filename>.network</filename> file is found, then all interfaces
+        which match the file are reconfigured.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index aec929a06a80f7e3524deab3041f3fdf818fe6f5..9fea2e0690e5f4e344695212398dbd778ea78fc0 100644 (file)
@@ -4,11 +4,24 @@
 
 #include "format-util.h"
 #include "memory-util.h"
+#include "stdio-util.h"
 
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
         /* Buffer is always cleared */
         memzero(buf, IF_NAMESIZE + 1);
-        return if_indextoname(ifindex, buf);
+        if (if_indextoname(ifindex, buf))
+                return buf;
+
+        if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
+                return NULL;
+
+        if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
+                snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+        else
+                snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+
+        return buf;
 }
 
 char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
index e0d184a541968f57af54ee9cb462f84fb5c217b8..59622508a333624a602895577361b76a2397ec0c 100644 (file)
 #  error Unknown ino_t size
 #endif
 
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
+typedef enum {
+        FORMAT_IFNAME_IFINDEX              = 1 << 0,
+        FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
+} FormatIfnameFlag;
+
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
+static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+        return format_ifname_full(ifindex, buf, 0);
+}
 
 typedef enum {
         FORMAT_BYTES_USE_IEC     = 1 << 0,
index b05626751e48aef1e3d685da0faaad4028b7ff6d..732b5b21f4527b39683077027127f979b7f8434a 100644 (file)
@@ -23,6 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 
         fflush(f);
         assert_se(manager_new(&manager) >= 0);
-        (void) network_load_one(manager, network_config);
+        (void) network_load_one(manager, &manager->networks, network_config);
         return 0;
 }
index 7853e197f1355a2c42118dcba8f206d358001384..9b5d3c0df3885b00aac1c074e1b8a27462ba1da2 100644 (file)
@@ -757,9 +757,10 @@ int netdev_load_one(Manager *manager, const char *filename) {
                 NetDev *n = hashmap_get(netdev->manager->netdevs, netdev->ifname);
 
                 assert(n);
-                log_netdev_warning_errno(netdev, r,
-                                         "The setting Name=%s in %s conflicts with the one in %s, ignoring",
-                                         netdev->ifname, netdev->filename, n->filename);
+                if (!streq(netdev->filename, n->filename))
+                        log_netdev_warning_errno(netdev, r,
+                                                 "The setting Name=%s in %s conflicts with the one in %s, ignoring",
+                                                 netdev->ifname, netdev->filename, n->filename);
 
                 /* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is
                  * removed from the hashmap 'manager->netdevs'. */
@@ -828,14 +829,15 @@ int netdev_load_one(Manager *manager, const char *filename) {
         return 0;
 }
 
-int netdev_load(Manager *manager) {
+int netdev_load(Manager *manager, bool reload) {
         _cleanup_strv_free_ char **files = NULL;
         char **f;
         int r;
 
         assert(manager);
 
-        hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
+        if (!reload)
+                hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
 
         r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS);
         if (r < 0)
index 989dd64ffc7126a7177afa3f8261a26f3f1d95e2..77cd15d8bfd1c860760576dc3736683fe9bb85a8 100644 (file)
@@ -156,7 +156,7 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
 /* For casting the various netdev kinds into a netdev */
 #define NETDEV(n) (&(n)->meta)
 
-int netdev_load(Manager *manager);
+int netdev_load(Manager *manager, bool reload);
 int netdev_load_one(Manager *manager, const char *filename);
 void netdev_drop(NetDev *netdev);
 
index 60d6a3f75fad5248bf3ad395187272a5d19557fc..d83dc611a5a150dcb156fa71d880712209d82ff6 100644 (file)
@@ -719,10 +719,8 @@ static int dump_gateways(
                 if (ifindex <= 0) {
                         char name[IF_NAMESIZE+1];
 
-                        if (format_ifname(local[i].ifindex, name))
-                                r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, name);
-                        else
-                                r = table_add_cell_stringf(table, NULL, "%s on %%%i", with_description ?: gateway, local[i].ifindex);
+                        r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway,
+                                                   format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
                 } else
                         r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
                 if (r < 0)
@@ -775,10 +773,8 @@ static int dump_addresses(
                 if (ifindex <= 0) {
                         char name[IF_NAMESIZE+1];
 
-                        if (format_ifname(local[i].ifindex, name))
-                                r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, name);
-                        else
-                                r = table_add_cell_stringf(table, NULL, "%s on %%%i", pretty, local[i].ifindex);
+                        r = table_add_cell_stringf(table, NULL, "%s on %s", pretty,
+                                                   format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
                 } else
                         r = table_add_cell(table, NULL, TABLE_STRING, pretty);
                 if (r < 0)
@@ -1759,14 +1755,13 @@ static int link_delete(int argc, char *argv[], void *userdata) {
         }
 
         SET_FOREACH(p, indexes, j) {
-                r = link_delete_send_message(rtnl, PTR_TO_INT(p));
+                index = PTR_TO_INT(p);
+                r = link_delete_send_message(rtnl, index);
                 if (r < 0) {
                         char ifname[IF_NAMESIZE + 1];
 
-                        if (format_ifname(index, ifname))
-                                return log_error_errno(r, "Failed to delete interface %s: %m", ifname);
-                        else
-                                return log_error_errno(r, "Failed to delete interface %d: %m", index);
+                        return log_error_errno(r, "Failed to delete interface %s: %m",
+                                               format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
                 }
         }
 
@@ -1815,6 +1810,73 @@ static int link_renew(int argc, char *argv[], void *userdata) {
 }
 
 
+static int verb_reload(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect system bus: %m");
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.network1",
+                        "/org/freedesktop/network1",
+                        "org.freedesktop.network1.Manager",
+                        "Reload",
+                        &error, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reload network settings: %m");
+
+        return 0;
+}
+
+static int verb_reconfigure(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_set_free_ Set *indexes = NULL;
+        int index, i, r;
+        Iterator j;
+        void *p;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect system bus: %m");
+
+        indexes = set_new(NULL);
+        if (!indexes)
+                return log_oom();
+
+        for (i = 1; i < argc; i++) {
+                r = parse_ifindex_or_ifname(argv[i], &index);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resolve interface %s", argv[i]);
+
+                r = set_put(indexes, INT_TO_PTR(index));
+                if (r < 0)
+                        return log_oom();
+        }
+
+        SET_FOREACH(p, indexes, j) {
+                index = PTR_TO_INT(p);
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.network1",
+                                "/org/freedesktop/network1",
+                                "org.freedesktop.network1.Manager",
+                                "ReconfigureLink",
+                                &error, NULL, "i", index);
+                if (r < 0) {
+                        char ifname[IF_NAMESIZE + 1];
+
+                        return log_error_errno(r, "Failed to reconfigure network interface %s: %m", format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
+                }
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -1826,19 +1888,21 @@ static int help(void) {
         printf("%s%s [OPTIONS...]\n\n"
                "Query and control the networking subsystem.%s\n"
                "\nCommands:\n"
-               "  list [PATTERN...]     List links\n"
-               "  status [PATTERN...]   Show link status\n"
-               "  lldp [PATTERN...]     Show LLDP neighbors\n"
-               "  label                 Show current address label entries in the kernel\n"
-               "  delete DEVICES...     Delete virtual netdevs\n"
-               "  renew DEVICES...      Renew dynamic configurations\n"
+               "  list [PATTERN...]      List links\n"
+               "  status [PATTERN...]    Show link status\n"
+               "  lldp [PATTERN...]      Show LLDP neighbors\n"
+               "  label                  Show current address label entries in the kernel\n"
+               "  delete DEVICES...      Delete virtual netdevs\n"
+               "  renew DEVICES...       Renew dynamic configurations\n"
+               "  reconfigure DEVICES... Reconfigure interfaces\n"
+               "  reload                 Reload .network and .netdev files\n"
                "\nOptions\n"
-               "  -h --help             Show this help\n"
-               "     --version          Show package version\n"
-               "     --no-pager         Do not pipe output into a pager\n"
-               "     --no-legend        Do not show the headers and footers\n"
-               "  -a --all              Show status for all links\n"
-               "  -s --stats            Show detailed link statics\n"
+               "  -h --help              Show this help\n"
+               "     --version           Show package version\n"
+               "     --no-pager          Do not pipe output into a pager\n"
+               "     --no-legend         Do not show the headers and footers\n"
+               "  -a --all               Show status for all links\n"
+               "  -s --stats             Show detailed link statics\n"
                "\nSee the %s for details.\n"
                , ansi_highlight()
                , program_invocation_short_name
@@ -1911,12 +1975,14 @@ static int parse_argv(int argc, char *argv[]) {
 
 static int networkctl_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
-                { "list",   VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links          },
-                { "status", VERB_ANY, VERB_ANY, 0,            link_status         },
-                { "lldp",   VERB_ANY, VERB_ANY, 0,            link_lldp_status    },
-                { "label",  VERB_ANY, VERB_ANY, 0,            list_address_labels },
-                { "delete", 2,        VERB_ANY, 0,            link_delete         },
-                { "renew",  2,        VERB_ANY, 0,            link_renew          },
+                { "list",        VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links          },
+                { "status",      VERB_ANY, VERB_ANY, 0,            link_status         },
+                { "lldp",        VERB_ANY, VERB_ANY, 0,            link_lldp_status    },
+                { "label",       VERB_ANY, VERB_ANY, 0,            list_address_labels },
+                { "delete",      2,        VERB_ANY, 0,            link_delete         },
+                { "renew",       2,        VERB_ANY, 0,            link_renew          },
+                { "reconfigure", 2,        VERB_ANY, 0,            verb_reconfigure    },
+                { "reload",      1,        1,        0,            verb_reload         },
                 {}
         };
 
index 9ef9146bcd7a9f5374f9285666754173550f319b..b0abc0a2169028b38bcd63b2e855329ede173405 100644 (file)
@@ -604,6 +604,29 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error
         return sd_bus_reply_method_return(message, NULL);
 }
 
+int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Link *l = userdata;
+        int r;
+
+        assert(message);
+        assert(l);
+
+        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+                                    "org.freedesktop.network1.reconfigure",
+                                    NULL, true, UID_INVALID,
+                                    &l->manager->polkit_registry, error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Polkit will call us back */
+
+        r = link_reconfigure(l, true);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable link_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -625,6 +648,7 @@ const sd_bus_vtable link_vtable[] = {
         SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_VTABLE_END
 };
index 1bea0b045367a33a7aba5af844b6a33ec0ebd254..09e4ad68a11606cec3262aeaf66281b6027c596f 100644 (file)
@@ -31,3 +31,4 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
 int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error);
index 5fedd3765c50c96a01384adb8744e1ba3bd08ef1..179408cbcc4a377d57255e29cd963c9c9617b842 100644 (file)
@@ -2857,7 +2857,7 @@ static int link_configure_duid(Link *link) {
         return 0;
 }
 
-int link_reconfigure(Link *link) {
+int link_reconfigure(Link *link, bool force) {
         Network *network;
         int r;
 
@@ -2875,7 +2875,7 @@ int link_reconfigure(Link *link) {
         } else if (r < 0)
                 return r;
 
-        if (link->network == network)
+        if (link->network == network && !force)
                 return 0;
 
         log_link_info(link, "Re-configuring with %s", network->filename);
@@ -3337,7 +3337,7 @@ static int link_carrier_gained(Link *link) {
         if (r < 0)
                 return r;
         if (r > 0) {
-                r = link_reconfigure(link);
+                r = link_reconfigure(link, false);
                 if (r < 0)
                         return r;
         }
index 8a96da90b284560f379ad0848db61dc113393333..b47557181a58013311c91fca537d55c9c533cdbd 100644 (file)
@@ -208,7 +208,7 @@ uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
 int link_request_set_routes(Link *link);
 int link_request_set_nexthop(Link *link);
 
-int link_reconfigure(Link *link);
+int link_reconfigure(Link *link, bool force);
 
 #define ADDRESS_FMT_VAL(address)                   \
         be32toh((address).s_addr) >> 24,           \
index 7484fcfa123fca573202e0a58d0f8593bd11a59d..773dcf0b4db71cf7dd140630138d83435c075684 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <net/if.h>
+#include <netinet/in.h>
+#include <sys/capability.h>
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
@@ -11,6 +13,7 @@
 #include "networkd-manager.h"
 #include "path-util.h"
 #include "strv.h"
+#include "user-util.h"
 
 static int method_list_links(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -187,6 +190,42 @@ static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus
         return call_link_method(userdata, message, bus_link_method_renew, error);
 }
 
+static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return call_link_method(userdata, message, bus_link_method_reconfigure, error);
+}
+
+static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *manager = userdata;
+        Iterator i;
+        Link *link;
+        int r;
+
+        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+                                    "org.freedesktop.network1.reload",
+                                    NULL, true, UID_INVALID,
+                                    &manager->polkit_registry, error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Polkit will call us back */
+
+        r = netdev_load(manager, true);
+        if (r < 0)
+                return r;
+
+        r = network_reload(manager);
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(link, manager->links, i) {
+                r = link_reconfigure(link, false);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -209,6 +248,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_VTABLE_END
 };
index c70194f1f85787a76540c3e249411a83c3119aa3..40fbf14743e8596c9daf23001fe670632cc60420 100644 (file)
@@ -1856,11 +1856,11 @@ int manager_load_config(Manager *m) {
         /* update timestamp */
         paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, true);
 
-        r = netdev_load(m);
+        r = netdev_load(m, false);
         if (r < 0)
                 return r;
 
-        r = network_load(m);
+        r = network_load(m, &m->networks);
         if (r < 0)
                 return r;
 
index 24d6556c7695fcee0d808d786399caa6d97a433f..85d615031265c3cbfa14f8665508bfa8153272ae 100644 (file)
@@ -314,7 +314,7 @@ int network_verify(Network *network) {
         return 0;
 }
 
-int network_load_one(Manager *manager, const char *filename) {
+int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
         _cleanup_free_ char *fname = NULL, *name = NULL;
         _cleanup_(network_unrefp) Network *network = NULL;
         _cleanup_fclose_ FILE *file = NULL;
@@ -488,36 +488,42 @@ int network_load_one(Manager *manager, const char *filename) {
                 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
                                   network->filename);
 
-        r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops);
+        struct stat stats;
+        if (stat(filename, &stats) < 0)
+                return -errno;
+        network->timestamp = timespec_load(&stats.st_mtim);
+
+        if (network_verify(network) < 0)
+                /* Ignore .network files that do not match the conditions. */
+                return 0;
+
+        r = ordered_hashmap_ensure_allocated(networks, &string_hash_ops);
         if (r < 0)
                 return r;
 
-        r = ordered_hashmap_put(manager->networks, network->name, network);
+        r = ordered_hashmap_put(*networks, network->name, network);
         if (r < 0)
                 return r;
 
-        if (network_verify(network) < 0)
-                return 0;
-
         network = NULL;
         return 0;
 }
 
-int network_load(Manager *manager) {
+int network_load(Manager *manager, OrderedHashmap **networks) {
         _cleanup_strv_free_ char **files = NULL;
         char **f;
         int r;
 
         assert(manager);
 
-        ordered_hashmap_clear_with_destructor(manager->networks, network_unref);
+        ordered_hashmap_clear_with_destructor(*networks, network_unref);
 
         r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate network files: %m");
 
         STRV_FOREACH(f, files) {
-                r = network_load_one(manager, *f);
+                r = network_load_one(manager, networks, *f);
                 if (r < 0)
                         log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
         }
@@ -525,6 +531,48 @@ int network_load(Manager *manager) {
         return 0;
 }
 
+int network_reload(Manager *manager) {
+        OrderedHashmap *new_networks = NULL;
+        Network *n, *old;
+        Iterator i;
+        int r;
+
+        assert(manager);
+
+        r = network_load(manager, &new_networks);
+        if (r < 0)
+                goto failure;
+
+        ORDERED_HASHMAP_FOREACH(n, new_networks, i) {
+                r = network_get_by_name(manager, n->name, &old);
+                if (r < 0)
+                        continue; /* The .network file is new. */
+
+                if (n->timestamp != old->timestamp)
+                        continue; /* The .network file is modified. */
+
+                if (!streq(n->filename, old->filename))
+                        continue;
+
+                r = ordered_hashmap_replace(new_networks, old->name, old);
+                if (r < 0)
+                        goto failure;
+
+                network_ref(old);
+                network_unref(n);
+        }
+
+        ordered_hashmap_free_with_destructor(manager->networks, network_unref);
+        manager->networks = new_networks;
+
+        return 0;
+
+failure:
+        ordered_hashmap_free_with_destructor(new_networks, network_unref);
+
+        return r;
+}
+
 static Network *network_free(Network *network) {
         IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
         RoutingPolicyRule *rule;
@@ -615,13 +663,9 @@ static Network *network_free(Network *network) {
         hashmap_free(network->prefixes_by_section);
         hashmap_free(network->rules_by_section);
 
-        if (network->manager) {
-                if (network->manager->networks && network->name)
-                        ordered_hashmap_remove(network->manager->networks, network->name);
-
-                if (network->manager->duids_requesting_uuid)
-                        set_remove(network->manager->duids_requesting_uuid, &network->duid);
-        }
+        if (network->manager &&
+            network->manager->duids_requesting_uuid)
+                set_remove(network->manager->duids_requesting_uuid, &network->duid);
 
         free(network->name);
 
index d169481cd851a6c0c4cd586d7ff27e173d03614f..1ae560e9943df7cc43a4c3e083e3f011709d2f63 100644 (file)
@@ -54,6 +54,7 @@ struct Network {
 
         char *filename;
         char *name;
+        usec_t timestamp;
 
         unsigned n_ref;
 
@@ -283,8 +284,9 @@ Network *network_ref(Network *network);
 Network *network_unref(Network *network);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_unref);
 
-int network_load(Manager *manager);
-int network_load_one(Manager *manager, const char *filename);
+int network_load(Manager *manager, OrderedHashmap **networks);
+int network_reload(Manager *manager);
+int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename);
 int network_verify(Network *network);
 
 int network_get_by_name(Manager *manager, const char *name, Network **ret);
index 4a33f5a8aadc7b9c3a4efc45bd51680ba0db67e4..9b1895e657d2912614dcd13352a6c6cb1ccde934 100644 (file)
                 <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
         </action>
 
+        <action id="org.freedesktop.network1.reload">
+                <description gettext-domain="systemd">Reload network settings</description>
+                <message gettext-domain="systemd">Authentication is required to reload network settings.</message>
+                <defaults>
+                        <allow_any>auth_admin</allow_any>
+                        <allow_inactive>auth_admin</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+                <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+        </action>
+
+        <action id="org.freedesktop.network1.reconfigure">
+                <description gettext-domain="systemd">Reconfigure network interface</description>
+                <message gettext-domain="systemd">Authentication is required to reconfigure network interface.</message>
+                <defaults>
+                        <allow_any>auth_admin</allow_any>
+                        <allow_inactive>auth_admin</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+                <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+        </action>
+
 </policyconfig>
index d1009217095c18b5a21410894e6bb7f9c3daad37..1cd623482f24bf13133524673982da6ac5ad479b 100755 (executable)
@@ -380,6 +380,7 @@ class Utilities():
 class NetworkctlTests(unittest.TestCase, Utilities):
 
     links = [
+        'dummy98',
         'test1',
         'veth99',
     ]
@@ -388,6 +389,8 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         '11-dummy.netdev',
         '11-dummy-mtu.netdev',
         '11-dummy.network',
+        '12-dummy.netdev',
+        '25-address-static.network',
         '25-veth.netdev',
         'netdev-link-local-addressing-yes.network',
     ]
@@ -401,6 +404,56 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         remove_unit_from_networkd_path(self.units)
         stop_networkd(show_logs=True)
 
+    def test_reconfigure(self):
+        copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('ip -4 address show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
+        self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
+        self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+
+        check_output('ip address del 10.1.2.3/16 dev dummy98')
+        check_output('ip address del 10.1.2.4/16 dev dummy98')
+        check_output('ip address del 10.2.2.4/16 dev dummy98')
+
+        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('ip -4 address show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
+        self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
+        self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+
+    def test_reload(self):
+        start_networkd(3)
+
+        copy_unit_to_networkd_unit_path('11-dummy.netdev')
+        check_output(*networkctl_cmd, 'reload', env=env)
+        time.sleep(3)
+        self.check_link_exists('test1')
+        self.check_operstate('test1', 'off', setup_state='unmanaged')
+
+        copy_unit_to_networkd_unit_path('11-dummy.network')
+        check_output(*networkctl_cmd, 'reload', env=env)
+        self.wait_online(['test1:degraded'])
+
+        remove_unit_from_networkd_path(['11-dummy.network'])
+        check_output(*networkctl_cmd, 'reload', env=env)
+        time.sleep(1)
+        self.check_operstate('test1', 'degraded', setup_state='unmanaged')
+
+        remove_unit_from_networkd_path(['11-dummy.netdev'])
+        check_output(*networkctl_cmd, 'reload', env=env)
+        self.check_operstate('test1', 'degraded', setup_state='unmanaged')
+
+        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
+        check_output(*networkctl_cmd, 'reload', env=env)
+        self.check_operstate('test1', 'degraded')
+
     def test_glob(self):
         copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
         start_networkd()