]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: move state file related functions to networkd-state-file.[ch]
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 21 Feb 2021 05:23:04 +0000 (14:23 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 3 Mar 2021 07:07:45 +0000 (16:07 +0900)
src/network/meson.build
src/network/networkd-dhcp4.c
src/network/networkd-link-bus.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-state-file.c [new file with mode: 0644]
src/network/networkd-state-file.h [new file with mode: 0644]

index 3ff04467b0d16ccc3adb94c8d84a1acfd2306b71..4fca3106dc9431ad32f0af3fed74a34d194346ad 100644 (file)
@@ -113,6 +113,8 @@ sources = files('''
         networkd-speed-meter.h
         networkd-sriov.c
         networkd-sriov.h
+        networkd-state-file.c
+        networkd-state-file.h
         networkd-sysctl.c
         networkd-sysctl.h
         networkd-util.c
index b0c783c774ac7b088461aae0ac1d18ac6b5d6dee..242b977f602759f7f794a5e87eae168f0013e8b9 100644 (file)
@@ -17,6 +17,7 @@
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-state-file.h"
 #include "string-table.h"
 #include "strv.h"
 #include "sysctl-util.h"
index 3ffe587a862f485feddee2dbf317e4eb1d9b6a7c..a23c98110e6facfd759248808912652f72404401 100644 (file)
@@ -13,6 +13,7 @@
 #include "networkd-link-bus.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-state-file.h"
 #include "parse-util.h"
 #include "resolve-util.h"
 #include "socket-netlink.h"
index 96d4d9235b8fd53fb0a855b6bcd1e87099fdf1d5..a8f021fa6dc53295457198337f0abba4b7e3cc5d 100644 (file)
@@ -42,6 +42,7 @@
 #include "networkd-sysctl.h"
 #include "networkd-radv.h"
 #include "networkd-routing-policy-rule.h"
+#include "networkd-state-file.h"
 #include "networkd-wifi.h"
 #include "set.h"
 #include "socket-util.h"
@@ -2806,390 +2807,6 @@ int link_update(Link *link, sd_netlink_message *m) {
         return 0;
 }
 
-static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
-        bool space = false;
-        Link *link;
-
-        assert(f);
-        assert(prefix);
-
-        if (hashmap_isempty(h))
-                return;
-
-        fputs(prefix, f);
-        HASHMAP_FOREACH(link, h) {
-                if (space)
-                        fputc(' ', f);
-
-                fprintf(f, "%i", link->ifindex);
-                space = true;
-        }
-
-        fputc('\n', f);
-}
-
-static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
-        for (unsigned j = 0; j < n_dns; j++) {
-                const char *str;
-
-                if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
-                        continue;
-
-                str = in_addr_full_to_string(dns[j]);
-                if (!str)
-                        continue;
-
-                if (*space)
-                        fputc(' ', f);
-                fputs(str, f);
-                *space = true;
-        }
-}
-
-static void serialize_addresses(
-                FILE *f,
-                const char *lvalue,
-                bool *space,
-                char **addresses,
-                sd_dhcp_lease *lease,
-                bool conditional,
-                sd_dhcp_lease_server_type_t what,
-                sd_dhcp6_lease *lease6,
-                bool conditional6,
-                int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
-                int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
-        int r;
-
-        bool _space = false;
-        if (!space)
-                space = &_space;
-
-        if (lvalue)
-                fprintf(f, "%s=", lvalue);
-        fputstrv(f, addresses, NULL, space);
-
-        if (lease && conditional) {
-                const struct in_addr *lease_addresses;
-
-                r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
-                if (r > 0)
-                        serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
-        }
-
-        if (lease6 && conditional6 && lease6_get_addr) {
-                const struct in6_addr *in6_addrs;
-
-                r = lease6_get_addr(lease6, &in6_addrs);
-                if (r > 0)
-                        serialize_in6_addrs(f, in6_addrs, r, space);
-        }
-
-        if (lease6 && conditional6 && lease6_get_fqdn) {
-                char **in6_hosts;
-
-                r = lease6_get_fqdn(lease6, &in6_hosts);
-                if (r > 0)
-                        fputstrv(f, in6_hosts, NULL, space);
-        }
-
-        if (lvalue)
-                fputc('\n', f);
-}
-
-int link_save(Link *link) {
-        const char *admin_state, *oper_state, *carrier_state, *address_state;
-        _cleanup_free_ char *temp_path = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        int r;
-
-        assert(link);
-        assert(link->state_file);
-        assert(link->lease_file);
-        assert(link->manager);
-
-        if (link->state == LINK_STATE_LINGER) {
-                (void) unlink(link->state_file);
-                return 0;
-        }
-
-        link_lldp_save(link);
-
-        admin_state = link_state_to_string(link->state);
-        assert(admin_state);
-
-        oper_state = link_operstate_to_string(link->operstate);
-        assert(oper_state);
-
-        carrier_state = link_carrier_state_to_string(link->carrier_state);
-        assert(carrier_state);
-
-        address_state = link_address_state_to_string(link->address_state);
-        assert(address_state);
-
-        r = fopen_temporary(link->state_file, &f, &temp_path);
-        if (r < 0)
-                goto fail;
-
-        (void) fchmod(fileno(f), 0644);
-
-        fprintf(f,
-                "# This is private data. Do not parse.\n"
-                "ADMIN_STATE=%s\n"
-                "OPER_STATE=%s\n"
-                "CARRIER_STATE=%s\n"
-                "ADDRESS_STATE=%s\n",
-                admin_state, oper_state, carrier_state, address_state);
-
-        if (link->network) {
-                char **dhcp6_domains = NULL, **dhcp_domains = NULL;
-                const char *dhcp_domainname = NULL, *p;
-                bool space;
-
-                fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
-                        yes_no(link->network->required_for_online));
-
-                LinkOperationalStateRange st = link->network->required_operstate_for_online;
-                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
-                        strempty(link_operstate_to_string(st.min)),
-                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
-                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
-
-                fprintf(f, "ACTIVATION_POLICY=%s\n",
-                        activation_policy_to_string(link->network->activation_policy));
-
-                fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
-
-                /************************************************************/
-
-                fputs("DNS=", f);
-                space = false;
-                if (link->n_dns != (unsigned) -1)
-                        link_save_dns(link, f, link->dns, link->n_dns, &space);
-                else
-                        link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
-
-                serialize_addresses(f, NULL, &space,
-                                    NULL,
-                                    link->dhcp_lease,
-                                    link->network->dhcp_use_dns,
-                                    SD_DHCP_LEASE_DNS,
-                                    link->dhcp6_lease,
-                                    link->network->dhcp6_use_dns,
-                                    sd_dhcp6_lease_get_dns,
-                                    NULL);
-
-                /* Make sure to flush out old entries before we use the NDisc data */
-                ndisc_vacuum(link);
-
-                if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) {
-                        NDiscRDNSS *dd;
-
-                        SET_FOREACH(dd, link->ndisc_rdnss)
-                                serialize_in6_addrs(f, &dd->address, 1, &space);
-                }
-
-                fputc('\n', f);
-
-                /************************************************************/
-
-                serialize_addresses(f, "NTP", NULL,
-                                    link->ntp ?: link->network->ntp,
-                                    link->dhcp_lease,
-                                    link->network->dhcp_use_ntp,
-                                    SD_DHCP_LEASE_NTP,
-                                    link->dhcp6_lease,
-                                    link->network->dhcp6_use_ntp,
-                                    sd_dhcp6_lease_get_ntp_addrs,
-                                    sd_dhcp6_lease_get_ntp_fqdn);
-
-                serialize_addresses(f, "SIP", NULL,
-                                    NULL,
-                                    link->dhcp_lease,
-                                    link->network->dhcp_use_sip,
-                                    SD_DHCP_LEASE_SIP,
-                                    NULL, false, NULL, NULL);
-
-                /************************************************************/
-
-                if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
-                        if (link->dhcp_lease) {
-                                (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
-                                (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
-                        }
-                        if (link->dhcp6_lease)
-                                (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains);
-                }
-
-                fputs("DOMAINS=", f);
-                space = false;
-                ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains)
-                        fputs_with_space(f, p, NULL, &space);
-
-                if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
-                        if (dhcp_domainname)
-                                fputs_with_space(f, dhcp_domainname, NULL, &space);
-                        if (dhcp_domains)
-                                fputstrv(f, dhcp_domains, NULL, &space);
-                        if (dhcp6_domains)
-                                fputstrv(f, dhcp6_domains, NULL, &space);
-                }
-
-                if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) {
-                        NDiscDNSSL *dd;
-
-                        SET_FOREACH(dd, link->ndisc_dnssl)
-                                fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
-                }
-
-                fputc('\n', f);
-
-                /************************************************************/
-
-                fputs("ROUTE_DOMAINS=", f);
-                space = false;
-                ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains)
-                        fputs_with_space(f, p, NULL, &space);
-
-                if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
-                        if (dhcp_domainname)
-                                fputs_with_space(f, dhcp_domainname, NULL, &space);
-                        if (dhcp_domains)
-                                fputstrv(f, dhcp_domains, NULL, &space);
-                        if (dhcp6_domains)
-                                fputstrv(f, dhcp6_domains, NULL, &space);
-                }
-
-                if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) {
-                        NDiscDNSSL *dd;
-
-                        SET_FOREACH(dd, link->ndisc_dnssl)
-                                fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
-                }
-
-                fputc('\n', f);
-
-                /************************************************************/
-
-                fprintf(f, "LLMNR=%s\n",
-                        resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
-
-                /************************************************************/
-
-                fprintf(f, "MDNS=%s\n",
-                        resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
-
-                /************************************************************/
-
-                int dns_default_route =
-                        link->dns_default_route >= 0 ? link->dns_default_route :
-                        link->network->dns_default_route;
-                if (dns_default_route >= 0)
-                        fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
-
-                /************************************************************/
-
-                DnsOverTlsMode dns_over_tls_mode =
-                        link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
-                        link->network->dns_over_tls_mode;
-                if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
-                        fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
-
-                /************************************************************/
-
-                DnssecMode dnssec_mode =
-                        link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
-                        link->network->dnssec_mode;
-                if (dnssec_mode != _DNSSEC_MODE_INVALID)
-                        fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
-
-                /************************************************************/
-
-                Set *nta_anchors = link->dnssec_negative_trust_anchors;
-                if (set_isempty(nta_anchors))
-                        nta_anchors = link->network->dnssec_negative_trust_anchors;
-
-                if (!set_isempty(nta_anchors)) {
-                        const char *n;
-
-                        fputs("DNSSEC_NTA=", f);
-                        space = false;
-                        SET_FOREACH(n, nta_anchors)
-                                fputs_with_space(f, n, NULL, &space);
-                        fputc('\n', f);
-                }
-        }
-
-        print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
-        print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
-
-        if (link->dhcp_lease) {
-                r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
-                if (r < 0)
-                        goto fail;
-
-                fprintf(f,
-                        "DHCP_LEASE=%s\n",
-                        link->lease_file);
-        } else
-                (void) unlink(link->lease_file);
-
-        r = link_serialize_dhcp6_client(link, f);
-        if (r < 0)
-                goto fail;
-
-        r = fflush_and_check(f);
-        if (r < 0)
-                goto fail;
-
-        r = conservative_rename(temp_path, link->state_file);
-        if (r < 0)
-                goto fail;
-
-        return 0;
-
-fail:
-        (void) unlink(link->state_file);
-        if (temp_path)
-                (void) unlink(temp_path);
-
-        return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
-}
-
-/* The serialized state in /run is no longer up-to-date. */
-void link_dirty(Link *link) {
-        int r;
-
-        assert(link);
-
-        /* mark manager dirty as link is dirty */
-        manager_dirty(link->manager);
-
-        r = set_ensure_put(&link->manager->dirty_links, NULL, link);
-        if (r <= 0)
-                /* Ignore allocation errors and don't take another ref if the link was already dirty */
-                return;
-        link_ref(link);
-}
-
-/* The serialized state in /run is up-to-date */
-void link_clean(Link *link) {
-        assert(link);
-        assert(link->manager);
-
-        link_unref(set_remove(link->manager->dirty_links, link));
-}
-
-int link_save_and_clean(Link *link) {
-        int r;
-
-        r = link_save(link);
-        if (r < 0)
-                return r;
-
-        link_clean(link);
-        return 0;
-}
-
 static const char* const link_state_table[_LINK_STATE_MAX] = {
         [LINK_STATE_PENDING] = "pending",
         [LINK_STATE_INITIALIZED] = "initialized",
index 8129fd2554cdf604b1222b28e055c6d7a2e6307b..347da1c928ccddf8fa1d9e38328b634044f1f782 100644 (file)
@@ -217,11 +217,6 @@ void link_check_ready(Link *link);
 void link_update_operstate(Link *link, bool also_update_bond_master);
 int link_update(Link *link, sd_netlink_message *message);
 
-void link_dirty(Link *link);
-void link_clean(Link *link);
-int link_save(Link *link);
-int link_save_and_clean(Link *link);
-
 int link_carrier_reset(Link *link);
 bool link_has_carrier(Link *link);
 
index 08da9120ad32592f2285a3ac8232cf37d82f424a..0b1cb1e42a389ef6bc29b0a9d15557c4f5aca308 100644 (file)
@@ -38,6 +38,7 @@
 #include "networkd-nexthop.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-speed-meter.h"
+#include "networkd-state-file.h"
 #include "ordered-set.h"
 #include "path-lookup.h"
 #include "path-util.h"
@@ -466,289 +467,6 @@ static int manager_connect_rtnl(Manager *m) {
         return 0;
 }
 
-static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
-        const char *p;
-        int r;
-
-        assert(s);
-        assert(dns);
-
-        if (dns->ifindex != 0 && dns->ifindex != ifindex)
-                return 0;
-
-        p = in_addr_full_to_string(dns);
-        if (!p)
-                return 0;
-
-        r = ordered_set_put_strdup(s, p);
-        if (r == -EEXIST)
-                return 0;
-
-        return r;
-}
-
-static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
-        int r, c = 0;
-
-        assert(s);
-        assert(dns || n == 0);
-
-        for (unsigned i = 0; i < n; i++) {
-                r = ordered_set_put_dns_server(s, ifindex, dns[i]);
-                if (r < 0)
-                        return r;
-
-                c += r;
-        }
-
-        return c;
-}
-
-static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) {
-        char *p;
-        int r;
-
-        assert(s);
-        assert(address);
-
-        r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
-        if (r < 0)
-                return r;
-
-        r = ordered_set_consume(s, p);
-        if (r == -EEXIST)
-                return 0;
-
-        return r;
-}
-
-static int ordered_set_put_in4_addrv(OrderedSet *s,
-                                     const struct in_addr *addresses,
-                                     size_t n,
-                                     bool (*predicate)(const struct in_addr *addr)) {
-        int r, c = 0;
-
-        assert(s);
-        assert(n == 0 || addresses);
-
-        for (size_t i = 0; i < n; i++) {
-                if (predicate && !predicate(&addresses[i]))
-                        continue;
-                r = ordered_set_put_in4_addr(s, addresses+i);
-                if (r < 0)
-                        return r;
-
-                c += r;
-        }
-
-        return c;
-}
-
-static int manager_save(Manager *m) {
-        _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
-        const char *operstate_str, *carrier_state_str, *address_state_str;
-        LinkOperationalState operstate = LINK_OPERSTATE_OFF;
-        LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
-        LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
-        _cleanup_free_ char *temp_path = NULL;
-        _cleanup_strv_free_ char **p = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        Link *link;
-        int r;
-
-        assert(m);
-        assert(m->state_file);
-
-        /* We add all NTP and DNS server to a set, to filter out duplicates */
-        dns = ordered_set_new(&string_hash_ops);
-        if (!dns)
-                return -ENOMEM;
-
-        ntp = ordered_set_new(&string_hash_ops);
-        if (!ntp)
-                return -ENOMEM;
-
-        sip = ordered_set_new(&string_hash_ops);
-        if (!sip)
-                return -ENOMEM;
-
-        search_domains = ordered_set_new(&dns_name_hash_ops);
-        if (!search_domains)
-                return -ENOMEM;
-
-        route_domains = ordered_set_new(&dns_name_hash_ops);
-        if (!route_domains)
-                return -ENOMEM;
-
-        HASHMAP_FOREACH(link, m->links) {
-                const struct in_addr *addresses;
-
-                if (link->flags & IFF_LOOPBACK)
-                        continue;
-
-                if (link->operstate > operstate)
-                        operstate = link->operstate;
-
-                if (link->carrier_state > carrier_state)
-                        carrier_state = link->carrier_state;
-
-                if (link->address_state > address_state)
-                        address_state = link->address_state;
-
-                if (!link->network)
-                        continue;
-
-                /* First add the static configured entries */
-                if (link->n_dns != (unsigned) -1)
-                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
-                else
-                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
-                if (r < 0)
-                        return r;
-
-                r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp);
-                if (r < 0)
-                        return r;
-
-                r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains);
-                if (r < 0)
-                        return r;
-
-                r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains);
-                if (r < 0)
-                        return r;
-
-                if (!link->dhcp_lease)
-                        continue;
-
-                /* Secondly, add the entries acquired via DHCP */
-                if (link->network->dhcp_use_dns) {
-                        r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
-                        if (r > 0) {
-                                r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local);
-                                if (r < 0)
-                                        return r;
-                        } else if (r < 0 && r != -ENODATA)
-                                return r;
-                }
-
-                if (link->network->dhcp_use_ntp) {
-                        r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
-                        if (r > 0) {
-                                r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local);
-                                if (r < 0)
-                                        return r;
-                        } else if (r < 0 && r != -ENODATA)
-                                return r;
-                }
-
-                if (link->network->dhcp_use_sip) {
-                        r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
-                        if (r > 0) {
-                                r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local);
-                                if (r < 0)
-                                        return r;
-                        } else if (r < 0 && r != -ENODATA)
-                                return r;
-                }
-
-                if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
-                        const char *domainname;
-                        char **domains = NULL;
-
-                        OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains;
-                        r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
-                        if (r >= 0) {
-                                r = ordered_set_put_strdup(target_domains, domainname);
-                                if (r < 0)
-                                        return r;
-                        } else if (r != -ENODATA)
-                                return r;
-
-                        r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
-                        if (r >= 0) {
-                                r = ordered_set_put_strdupv(target_domains, domains);
-                                if (r < 0)
-                                        return r;
-                        } else if (r != -ENODATA)
-                                return r;
-                }
-        }
-
-        if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
-                carrier_state = LINK_CARRIER_STATE_CARRIER;
-
-        operstate_str = link_operstate_to_string(operstate);
-        assert(operstate_str);
-
-        carrier_state_str = link_carrier_state_to_string(carrier_state);
-        assert(carrier_state_str);
-
-        address_state_str = link_address_state_to_string(address_state);
-        assert(address_state_str);
-
-        r = fopen_temporary(m->state_file, &f, &temp_path);
-        if (r < 0)
-                return r;
-
-        (void) fchmod(fileno(f), 0644);
-
-        fprintf(f,
-                "# This is private data. Do not parse.\n"
-                "OPER_STATE=%s\n"
-                "CARRIER_STATE=%s\n"
-                "ADDRESS_STATE=%s\n",
-                operstate_str, carrier_state_str, address_state_str);
-
-        ordered_set_print(f, "DNS=", dns);
-        ordered_set_print(f, "NTP=", ntp);
-        ordered_set_print(f, "SIP=", sip);
-        ordered_set_print(f, "DOMAINS=", search_domains);
-        ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
-
-        r = fflush_and_check(f);
-        if (r < 0)
-                goto fail;
-
-        r = conservative_rename(temp_path, m->state_file);
-        if (r < 0)
-                goto fail;
-
-        if (m->operational_state != operstate) {
-                m->operational_state = operstate;
-                if (strv_extend(&p, "OperationalState") < 0)
-                        log_oom();
-        }
-
-        if (m->carrier_state != carrier_state) {
-                m->carrier_state = carrier_state;
-                if (strv_extend(&p, "CarrierState") < 0)
-                        log_oom();
-        }
-
-        if (m->address_state != address_state) {
-                m->address_state = address_state;
-                if (strv_extend(&p, "AddressState") < 0)
-                        log_oom();
-        }
-
-        if (p) {
-                r = manager_send_changed_strv(m, p);
-                if (r < 0)
-                        log_error_errno(r, "Could not emit changed properties: %m");
-        }
-
-        m->dirty = false;
-
-        return 0;
-
-fail:
-        (void) unlink(m->state_file);
-        (void) unlink(temp_path);
-
-        return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
-}
-
 static int manager_dirty_handler(sd_event_source *s, void *userdata) {
         Manager *m = userdata;
         Link *link;
@@ -1155,13 +873,6 @@ Link* manager_find_uplink(Manager *m, Link *exclude) {
         return NULL;
 }
 
-void manager_dirty(Manager *manager) {
-        assert(manager);
-
-        /* the serialized state in /run is no longer up-to-date */
-        manager->dirty = true;
-}
-
 static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
         const sd_bus_error *e;
         int r;
index c0cd4275170d08a02352efe02987cb94bb43c017..929855daaf1bd2b1300fd0a9e6944d10981ae58a 100644 (file)
@@ -100,8 +100,6 @@ bool manager_should_reload(Manager *m);
 
 int manager_enumerate(Manager *m);
 
-void manager_dirty(Manager *m);
-
 Link* manager_find_uplink(Manager *m, Link *exclude);
 
 int manager_set_hostname(Manager *m, const char *hostname);
index acc1de66d2a2ea7e900eeb87ad076a17183d7317..bb62ddd93fde2e84ab34ccf1437e6a12f90a5eb2 100644 (file)
@@ -15,6 +15,7 @@
 #include "networkd-dhcp6.h"
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
+#include "networkd-state-file.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c
new file mode 100644 (file)
index 0000000..7cf1948
--- /dev/null
@@ -0,0 +1,690 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <linux/if.h>
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "network-internal.h"
+#include "networkd-link.h"
+#include "networkd-manager-bus.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
+#include "networkd-state-file.h"
+#include "ordered-set.h"
+#include "set.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
+        const char *p;
+        int r;
+
+        assert(s);
+        assert(dns);
+
+        if (dns->ifindex != 0 && dns->ifindex != ifindex)
+                return 0;
+
+        p = in_addr_full_to_string(dns);
+        if (!p)
+                return 0;
+
+        r = ordered_set_put_strdup(s, p);
+        if (r == -EEXIST)
+                return 0;
+
+        return r;
+}
+
+static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
+        int r, c = 0;
+
+        assert(s);
+        assert(dns || n == 0);
+
+        for (unsigned i = 0; i < n; i++) {
+                r = ordered_set_put_dns_server(s, ifindex, dns[i]);
+                if (r < 0)
+                        return r;
+
+                c += r;
+        }
+
+        return c;
+}
+
+static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) {
+        char *p;
+        int r;
+
+        assert(s);
+        assert(address);
+
+        r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p);
+        if (r < 0)
+                return r;
+
+        r = ordered_set_consume(s, p);
+        if (r == -EEXIST)
+                return 0;
+
+        return r;
+}
+
+static int ordered_set_put_in4_addrv(OrderedSet *s,
+                                     const struct in_addr *addresses,
+                                     size_t n,
+                                     bool (*predicate)(const struct in_addr *addr)) {
+        int r, c = 0;
+
+        assert(s);
+        assert(n == 0 || addresses);
+
+        for (size_t i = 0; i < n; i++) {
+                if (predicate && !predicate(&addresses[i]))
+                        continue;
+                r = ordered_set_put_in4_addr(s, addresses+i);
+                if (r < 0)
+                        return r;
+
+                c += r;
+        }
+
+        return c;
+}
+
+int manager_save(Manager *m) {
+        _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
+        const char *operstate_str, *carrier_state_str, *address_state_str;
+        LinkOperationalState operstate = LINK_OPERSTATE_OFF;
+        LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
+        LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_strv_free_ char **p = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        Link *link;
+        int r;
+
+        assert(m);
+        assert(m->state_file);
+
+        /* We add all NTP and DNS server to a set, to filter out duplicates */
+        dns = ordered_set_new(&string_hash_ops);
+        if (!dns)
+                return -ENOMEM;
+
+        ntp = ordered_set_new(&string_hash_ops);
+        if (!ntp)
+                return -ENOMEM;
+
+        sip = ordered_set_new(&string_hash_ops);
+        if (!sip)
+                return -ENOMEM;
+
+        search_domains = ordered_set_new(&dns_name_hash_ops);
+        if (!search_domains)
+                return -ENOMEM;
+
+        route_domains = ordered_set_new(&dns_name_hash_ops);
+        if (!route_domains)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH(link, m->links) {
+                const struct in_addr *addresses;
+
+                if (link->flags & IFF_LOOPBACK)
+                        continue;
+
+                if (link->operstate > operstate)
+                        operstate = link->operstate;
+
+                if (link->carrier_state > carrier_state)
+                        carrier_state = link->carrier_state;
+
+                if (link->address_state > address_state)
+                        address_state = link->address_state;
+
+                if (!link->network)
+                        continue;
+
+                /* First add the static configured entries */
+                if (link->n_dns != (unsigned) -1)
+                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
+                else
+                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
+                if (r < 0)
+                        return r;
+
+                r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp);
+                if (r < 0)
+                        return r;
+
+                r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains);
+                if (r < 0)
+                        return r;
+
+                r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains);
+                if (r < 0)
+                        return r;
+
+                if (!link->dhcp_lease)
+                        continue;
+
+                /* Secondly, add the entries acquired via DHCP */
+                if (link->network->dhcp_use_dns) {
+                        r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
+                        if (r > 0) {
+                                r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local);
+                                if (r < 0)
+                                        return r;
+                        } else if (r < 0 && r != -ENODATA)
+                                return r;
+                }
+
+                if (link->network->dhcp_use_ntp) {
+                        r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
+                        if (r > 0) {
+                                r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local);
+                                if (r < 0)
+                                        return r;
+                        } else if (r < 0 && r != -ENODATA)
+                                return r;
+                }
+
+                if (link->network->dhcp_use_sip) {
+                        r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
+                        if (r > 0) {
+                                r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local);
+                                if (r < 0)
+                                        return r;
+                        } else if (r < 0 && r != -ENODATA)
+                                return r;
+                }
+
+                if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+                        const char *domainname;
+                        char **domains = NULL;
+
+                        OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains;
+                        r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
+                        if (r >= 0) {
+                                r = ordered_set_put_strdup(target_domains, domainname);
+                                if (r < 0)
+                                        return r;
+                        } else if (r != -ENODATA)
+                                return r;
+
+                        r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
+                        if (r >= 0) {
+                                r = ordered_set_put_strdupv(target_domains, domains);
+                                if (r < 0)
+                                        return r;
+                        } else if (r != -ENODATA)
+                                return r;
+                }
+        }
+
+        if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
+                carrier_state = LINK_CARRIER_STATE_CARRIER;
+
+        operstate_str = link_operstate_to_string(operstate);
+        assert(operstate_str);
+
+        carrier_state_str = link_carrier_state_to_string(carrier_state);
+        assert(carrier_state_str);
+
+        address_state_str = link_address_state_to_string(address_state);
+        assert(address_state_str);
+
+        r = fopen_temporary(m->state_file, &f, &temp_path);
+        if (r < 0)
+                return r;
+
+        (void) fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "OPER_STATE=%s\n"
+                "CARRIER_STATE=%s\n"
+                "ADDRESS_STATE=%s\n",
+                operstate_str, carrier_state_str, address_state_str);
+
+        ordered_set_print(f, "DNS=", dns);
+        ordered_set_print(f, "NTP=", ntp);
+        ordered_set_print(f, "SIP=", sip);
+        ordered_set_print(f, "DOMAINS=", search_domains);
+        ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto fail;
+
+        r = conservative_rename(temp_path, m->state_file);
+        if (r < 0)
+                goto fail;
+
+        if (m->operational_state != operstate) {
+                m->operational_state = operstate;
+                if (strv_extend(&p, "OperationalState") < 0)
+                        log_oom();
+        }
+
+        if (m->carrier_state != carrier_state) {
+                m->carrier_state = carrier_state;
+                if (strv_extend(&p, "CarrierState") < 0)
+                        log_oom();
+        }
+
+        if (m->address_state != address_state) {
+                m->address_state = address_state;
+                if (strv_extend(&p, "AddressState") < 0)
+                        log_oom();
+        }
+
+        if (p) {
+                r = manager_send_changed_strv(m, p);
+                if (r < 0)
+                        log_error_errno(r, "Could not emit changed properties: %m");
+        }
+
+        m->dirty = false;
+
+        return 0;
+
+fail:
+        (void) unlink(m->state_file);
+        (void) unlink(temp_path);
+
+        return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
+}
+
+static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
+        bool space = false;
+        Link *link;
+
+        assert(f);
+        assert(prefix);
+
+        if (hashmap_isempty(h))
+                return;
+
+        fputs(prefix, f);
+        HASHMAP_FOREACH(link, h) {
+                if (space)
+                        fputc(' ', f);
+
+                fprintf(f, "%i", link->ifindex);
+                space = true;
+        }
+
+        fputc('\n', f);
+}
+
+static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
+        for (unsigned j = 0; j < n_dns; j++) {
+                const char *str;
+
+                if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
+                        continue;
+
+                str = in_addr_full_to_string(dns[j]);
+                if (!str)
+                        continue;
+
+                if (*space)
+                        fputc(' ', f);
+                fputs(str, f);
+                *space = true;
+        }
+}
+
+static void serialize_addresses(
+                FILE *f,
+                const char *lvalue,
+                bool *space,
+                char **addresses,
+                sd_dhcp_lease *lease,
+                bool conditional,
+                sd_dhcp_lease_server_type_t what,
+                sd_dhcp6_lease *lease6,
+                bool conditional6,
+                int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
+                int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
+        int r;
+
+        bool _space = false;
+        if (!space)
+                space = &_space;
+
+        if (lvalue)
+                fprintf(f, "%s=", lvalue);
+        fputstrv(f, addresses, NULL, space);
+
+        if (lease && conditional) {
+                const struct in_addr *lease_addresses;
+
+                r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
+                if (r > 0)
+                        serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
+        }
+
+        if (lease6 && conditional6 && lease6_get_addr) {
+                const struct in6_addr *in6_addrs;
+
+                r = lease6_get_addr(lease6, &in6_addrs);
+                if (r > 0)
+                        serialize_in6_addrs(f, in6_addrs, r, space);
+        }
+
+        if (lease6 && conditional6 && lease6_get_fqdn) {
+                char **in6_hosts;
+
+                r = lease6_get_fqdn(lease6, &in6_hosts);
+                if (r > 0)
+                        fputstrv(f, in6_hosts, NULL, space);
+        }
+
+        if (lvalue)
+                fputc('\n', f);
+}
+
+int link_save(Link *link) {
+        const char *admin_state, *oper_state, *carrier_state, *address_state;
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(link);
+        assert(link->state_file);
+        assert(link->lease_file);
+        assert(link->manager);
+
+        if (link->state == LINK_STATE_LINGER) {
+                (void) unlink(link->state_file);
+                return 0;
+        }
+
+        link_lldp_save(link);
+
+        admin_state = link_state_to_string(link->state);
+        assert(admin_state);
+
+        oper_state = link_operstate_to_string(link->operstate);
+        assert(oper_state);
+
+        carrier_state = link_carrier_state_to_string(link->carrier_state);
+        assert(carrier_state);
+
+        address_state = link_address_state_to_string(link->address_state);
+        assert(address_state);
+
+        r = fopen_temporary(link->state_file, &f, &temp_path);
+        if (r < 0)
+                goto fail;
+
+        (void) fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "ADMIN_STATE=%s\n"
+                "OPER_STATE=%s\n"
+                "CARRIER_STATE=%s\n"
+                "ADDRESS_STATE=%s\n",
+                admin_state, oper_state, carrier_state, address_state);
+
+        if (link->network) {
+                char **dhcp6_domains = NULL, **dhcp_domains = NULL;
+                const char *dhcp_domainname = NULL, *p;
+                bool space;
+
+                fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
+                        yes_no(link->network->required_for_online));
+
+                LinkOperationalStateRange st = link->network->required_operstate_for_online;
+                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
+                        strempty(link_operstate_to_string(st.min)),
+                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
+                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
+
+                fprintf(f, "ACTIVATION_POLICY=%s\n",
+                        activation_policy_to_string(link->network->activation_policy));
+
+                fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
+
+                /************************************************************/
+
+                fputs("DNS=", f);
+                space = false;
+                if (link->n_dns != (unsigned) -1)
+                        link_save_dns(link, f, link->dns, link->n_dns, &space);
+                else
+                        link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
+
+                serialize_addresses(f, NULL, &space,
+                                    NULL,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_dns,
+                                    SD_DHCP_LEASE_DNS,
+                                    link->dhcp6_lease,
+                                    link->network->dhcp6_use_dns,
+                                    sd_dhcp6_lease_get_dns,
+                                    NULL);
+
+                /* Make sure to flush out old entries before we use the NDisc data */
+                ndisc_vacuum(link);
+
+                if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) {
+                        NDiscRDNSS *dd;
+
+                        SET_FOREACH(dd, link->ndisc_rdnss)
+                                serialize_in6_addrs(f, &dd->address, 1, &space);
+                }
+
+                fputc('\n', f);
+
+                /************************************************************/
+
+                serialize_addresses(f, "NTP", NULL,
+                                    link->ntp ?: link->network->ntp,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_ntp,
+                                    SD_DHCP_LEASE_NTP,
+                                    link->dhcp6_lease,
+                                    link->network->dhcp6_use_ntp,
+                                    sd_dhcp6_lease_get_ntp_addrs,
+                                    sd_dhcp6_lease_get_ntp_fqdn);
+
+                serialize_addresses(f, "SIP", NULL,
+                                    NULL,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_sip,
+                                    SD_DHCP_LEASE_SIP,
+                                    NULL, false, NULL, NULL);
+
+                /************************************************************/
+
+                if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+                        if (link->dhcp_lease) {
+                                (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
+                                (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
+                        }
+                        if (link->dhcp6_lease)
+                                (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains);
+                }
+
+                fputs("DOMAINS=", f);
+                space = false;
+                ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains)
+                        fputs_with_space(f, p, NULL, &space);
+
+                if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) {
+                        if (dhcp_domainname)
+                                fputs_with_space(f, dhcp_domainname, NULL, &space);
+                        if (dhcp_domains)
+                                fputstrv(f, dhcp_domains, NULL, &space);
+                        if (dhcp6_domains)
+                                fputstrv(f, dhcp6_domains, NULL, &space);
+                }
+
+                if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) {
+                        NDiscDNSSL *dd;
+
+                        SET_FOREACH(dd, link->ndisc_dnssl)
+                                fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
+                }
+
+                fputc('\n', f);
+
+                /************************************************************/
+
+                fputs("ROUTE_DOMAINS=", f);
+                space = false;
+                ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains)
+                        fputs_with_space(f, p, NULL, &space);
+
+                if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) {
+                        if (dhcp_domainname)
+                                fputs_with_space(f, dhcp_domainname, NULL, &space);
+                        if (dhcp_domains)
+                                fputstrv(f, dhcp_domains, NULL, &space);
+                        if (dhcp6_domains)
+                                fputstrv(f, dhcp6_domains, NULL, &space);
+                }
+
+                if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) {
+                        NDiscDNSSL *dd;
+
+                        SET_FOREACH(dd, link->ndisc_dnssl)
+                                fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
+                }
+
+                fputc('\n', f);
+
+                /************************************************************/
+
+                fprintf(f, "LLMNR=%s\n",
+                        resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
+
+                /************************************************************/
+
+                fprintf(f, "MDNS=%s\n",
+                        resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
+
+                /************************************************************/
+
+                int dns_default_route =
+                        link->dns_default_route >= 0 ? link->dns_default_route :
+                        link->network->dns_default_route;
+                if (dns_default_route >= 0)
+                        fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
+
+                /************************************************************/
+
+                DnsOverTlsMode dns_over_tls_mode =
+                        link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
+                        link->network->dns_over_tls_mode;
+                if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+                        fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
+
+                /************************************************************/
+
+                DnssecMode dnssec_mode =
+                        link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
+                        link->network->dnssec_mode;
+                if (dnssec_mode != _DNSSEC_MODE_INVALID)
+                        fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
+
+                /************************************************************/
+
+                Set *nta_anchors = link->dnssec_negative_trust_anchors;
+                if (set_isempty(nta_anchors))
+                        nta_anchors = link->network->dnssec_negative_trust_anchors;
+
+                if (!set_isempty(nta_anchors)) {
+                        const char *n;
+
+                        fputs("DNSSEC_NTA=", f);
+                        space = false;
+                        SET_FOREACH(n, nta_anchors)
+                                fputs_with_space(f, n, NULL, &space);
+                        fputc('\n', f);
+                }
+        }
+
+        print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
+        print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
+
+        if (link->dhcp_lease) {
+                r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
+                if (r < 0)
+                        goto fail;
+
+                fprintf(f,
+                        "DHCP_LEASE=%s\n",
+                        link->lease_file);
+        } else
+                (void) unlink(link->lease_file);
+
+        r = link_serialize_dhcp6_client(link, f);
+        if (r < 0)
+                goto fail;
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto fail;
+
+        r = conservative_rename(temp_path, link->state_file);
+        if (r < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        (void) unlink(link->state_file);
+        if (temp_path)
+                (void) unlink(temp_path);
+
+        return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
+}
+
+void link_dirty(Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        /* The serialized state in /run is no longer up-to-date. */
+
+        /* Also mark manager dirty as link is dirty */
+        link->manager->dirty = true;
+
+        r = set_ensure_put(&link->manager->dirty_links, NULL, link);
+        if (r <= 0)
+                /* Ignore allocation errors and don't take another ref if the link was already dirty */
+                return;
+        link_ref(link);
+}
+
+void link_clean(Link *link) {
+        assert(link);
+        assert(link->manager);
+
+        /* The serialized state in /run is up-to-date */
+
+        link_unref(set_remove(link->manager->dirty_links, link));
+}
+
+int link_save_and_clean(Link *link) {
+        int r;
+
+        r = link_save(link);
+        if (r < 0)
+                return r;
+
+        link_clean(link);
+        return 0;
+}
diff --git a/src/network/networkd-state-file.h b/src/network/networkd-state-file.h
new file mode 100644 (file)
index 0000000..44a109f
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+typedef struct Link Link;
+typedef struct Manager Manager;
+
+void link_dirty(Link *link);
+void link_clean(Link *link);
+int link_save(Link *link);
+int link_save_and_clean(Link *link);
+
+int manager_save(Manager *m);