]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-state-file.c
io.systemd.Unit.List fix context/runtime split (#38172)
[thirdparty/systemd.git] / src / network / networkd-state-file.c
CommitLineData
3b5a4fc6
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
baa3fadf 3#include <net/if.h>
1cf40697 4#include <netinet/in.h>
baa3fadf
DDM
5#include <sys/stat.h>
6#include <unistd.h>
7
8#include "sd-dhcp6-lease.h"
3b5a4fc6
YW
9
10#include "alloc-util.h"
2c154c54 11#include "dns-domain.h"
b0e71631 12#include "dns-resolver-internal.h"
baa3fadf 13#include "errno-util.h"
a2640646 14#include "escape.h"
3b5a4fc6
YW
15#include "fd-util.h"
16#include "fileio.h"
17#include "fs-util.h"
18#include "network-internal.h"
6341ea54 19#include "networkd-dhcp-common.h"
3b5a4fc6 20#include "networkd-link.h"
3b5a4fc6 21#include "networkd-manager.h"
1cf40697 22#include "networkd-manager-bus.h"
3b5a4fc6 23#include "networkd-network.h"
d12fb2bc 24#include "networkd-ntp.h"
3b5a4fc6
YW
25#include "networkd-state-file.h"
26#include "ordered-set.h"
27#include "set.h"
baa3fadf 28#include "string-util.h"
3b5a4fc6
YW
29#include "strv.h"
30#include "tmpfile-util.h"
31
86a66e9b 32static int ordered_set_put_dns_servers(OrderedSet **s, int ifindex, struct in_addr_full **dns, unsigned n) {
3b5a4fc6
YW
33 int r;
34
35 assert(s);
86a66e9b 36 assert(dns || n == 0);
3b5a4fc6 37
86a66e9b
YW
38 FOREACH_ARRAY(a, dns, n) {
39 const char *p;
3b5a4fc6 40
86a66e9b
YW
41 if ((*a)->ifindex != 0 && (*a)->ifindex != ifindex)
42 return 0;
3b5a4fc6 43
86a66e9b
YW
44 p = in_addr_full_to_string(*a);
45 if (!p)
46 return 0;
47
48 r = ordered_set_put_strdup(s, p);
49 if (r < 0)
50 return r;
51 }
3b5a4fc6 52
86a66e9b 53 return 0;
3b5a4fc6
YW
54}
55
86a66e9b
YW
56static int ordered_set_put_in4_addrv(
57 OrderedSet **s,
58 const struct in_addr *addresses,
59 size_t n,
60 bool (*predicate)(const struct in_addr *addr)) {
61
62 int r;
3b5a4fc6
YW
63
64 assert(s);
86a66e9b 65 assert(n == 0 || addresses);
3b5a4fc6 66
86a66e9b
YW
67 FOREACH_ARRAY(a, addresses, n) {
68 if (predicate && !predicate(a))
69 continue;
70
71 r = ordered_set_put_strdup(s, IN4_ADDR_TO_STRING(a));
3b5a4fc6
YW
72 if (r < 0)
73 return r;
86a66e9b
YW
74 }
75
76 return 0;
77}
78
79static int ordered_set_put_in6_addrv(
80 OrderedSet **s,
81 const struct in6_addr *addresses,
82 size_t n) {
83
84 int r;
85
86 assert(s);
87 assert(n == 0 || addresses);
3b5a4fc6 88
86a66e9b
YW
89 FOREACH_ARRAY(a, addresses, n) {
90 r = ordered_set_put_strdup(s, IN6_ADDR_TO_STRING(a));
91 if (r < 0)
92 return r;
3b5a4fc6
YW
93 }
94
86a66e9b 95 return 0;
3b5a4fc6
YW
96}
97
86a66e9b 98static int link_put_dns(Link *link, OrderedSet **s) {
3b5a4fc6
YW
99 int r;
100
86a66e9b
YW
101 assert(link);
102 assert(link->network);
3b5a4fc6 103 assert(s);
3b5a4fc6 104
86a66e9b
YW
105 if (link->n_dns != UINT_MAX)
106 return ordered_set_put_dns_servers(s, link->ifindex, link->dns, link->n_dns);
107
108 r = ordered_set_put_dns_servers(s, link->ifindex, link->network->dns, link->network->n_dns);
3b5a4fc6
YW
109 if (r < 0)
110 return r;
111
9646ffe2 112 if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
86a66e9b
YW
113 const struct in_addr *addresses;
114
115 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
116 if (r >= 0) {
117 r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
118 if (r < 0)
119 return r;
120 }
121 }
122
3fd6708c
RP
123 if (link->dhcp_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
124 sd_dns_resolver *resolvers;
125
126 r = sd_dhcp_lease_get_dnr(link->dhcp_lease, &resolvers);
127 if (r >= 0) {
128 struct in_addr_full **dot_servers;
129 size_t n = 0;
130 CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
131
132 r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
133 if (r < 0)
134 return r;
135 r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
136 if (r < 0)
137 return r;
138 }
139 }
140
9646ffe2 141 if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
86a66e9b
YW
142 const struct in6_addr *addresses;
143
144 r = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &addresses);
145 if (r >= 0) {
146 r = ordered_set_put_in6_addrv(s, addresses, r);
147 if (r < 0)
148 return r;
149 }
150 }
151
168ad243
RP
152 if (link->dhcp6_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
153 sd_dns_resolver *resolvers;
154
155 r = sd_dhcp6_lease_get_dnr(link->dhcp6_lease, &resolvers);
156 if (r >= 0 ) {
157 struct in_addr_full **dot_servers;
158 size_t n = 0;
159 CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
160
161 r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
162 if (r < 0)
163 return r;
164
165 r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
166 if (r < 0)
167 return r;
168 }
169 }
170
9646ffe2 171 if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
86a66e9b
YW
172 NDiscRDNSS *a;
173
174 SET_FOREACH(a, link->ndisc_rdnss) {
64761f38 175 r = ordered_set_put_in6_addrv(s, &a->address, 1);
86a66e9b
YW
176 if (r < 0)
177 return r;
178 }
179 }
180
65187c46
RP
181 if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) {
182 NDiscDNR *a;
183
184 SET_FOREACH(a, link->ndisc_dnr) {
185 struct in_addr_full **dot_servers = NULL;
186 size_t n = 0;
187 CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
188
189 r = dns_resolvers_to_dot_addrs(&a->resolver, 1, &dot_servers, &n);
190 if (r < 0)
191 return r;
192
193 r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
194 if (r < 0)
195 return r;
196 }
197 }
198
86a66e9b
YW
199 return 0;
200}
201
202static int link_put_ntp(Link *link, OrderedSet **s) {
203 int r;
204
205 assert(link);
206 assert(link->network);
207 assert(s);
208
209 if (link->ntp)
210 return ordered_set_put_strdupv(s, link->ntp);
211
212 r = ordered_set_put_strdupv(s, link->network->ntp);
cf453507
YW
213 if (r < 0)
214 return r;
215
d12fb2bc 216 if (link->dhcp_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
86a66e9b
YW
217 const struct in_addr *addresses;
218
219 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
220 if (r >= 0) {
221 r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
222 if (r < 0)
223 return r;
224 }
225 }
226
d12fb2bc 227 if (link->dhcp6_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
86a66e9b
YW
228 const struct in6_addr *addresses;
229 char **fqdn;
3b5a4fc6 230
86a66e9b
YW
231 r = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &addresses);
232 if (r >= 0) {
233 r = ordered_set_put_in6_addrv(s, addresses, r);
234 if (r < 0)
235 return r;
236 }
237
238 r = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &fqdn);
239 if (r >= 0) {
240 r = ordered_set_put_strdupv(s, fqdn);
241 if (r < 0)
242 return r;
243 }
244 }
245
246 return 0;
3b5a4fc6
YW
247}
248
86a66e9b
YW
249static int link_put_sip(Link *link, OrderedSet **s) {
250 int r;
251
252 assert(link);
253 assert(link->network);
254 assert(s);
255
e188243d 256 if (link->dhcp_lease && link->network->dhcp_use_sip) {
86a66e9b
YW
257 const struct in_addr *addresses;
258
259 r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
260 if (r >= 0) {
261 r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
262 if (r < 0)
263 return r;
264 }
265 }
266
267 return 0;
268}
44e1f7e3 269
86a66e9b
YW
270static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
271 OrderedSet *link_domains, *network_domains;
78f5c649 272 UseDomains use_domains;
86a66e9b 273 int r;
3b5a4fc6 274
86a66e9b
YW
275 assert(link);
276 assert(link->network);
3b5a4fc6 277 assert(s);
3b5a4fc6 278
86a66e9b
YW
279 link_domains = is_route ? link->route_domains : link->search_domains;
280 network_domains = is_route ? link->network->route_domains : link->network->search_domains;
78f5c649 281 use_domains = is_route ? USE_DOMAINS_ROUTE : USE_DOMAINS_YES;
86a66e9b
YW
282
283 if (link_domains)
2c154c54 284 return ordered_set_put_string_set_full(s, &dns_name_hash_ops_free, link_domains);
86a66e9b 285
2c154c54 286 r = ordered_set_put_string_set_full(s, &dns_name_hash_ops_free, network_domains);
86a66e9b
YW
287 if (r < 0)
288 return r;
289
7a169cb4 290 if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) {
86a66e9b
YW
291 const char *domainname;
292 char **domains;
293
294 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
295 if (r >= 0) {
2c154c54 296 r = ordered_set_put_strdup_full(s, &dns_name_hash_ops_free, domainname);
86a66e9b
YW
297 if (r < 0)
298 return r;
299 }
3b5a4fc6 300
86a66e9b
YW
301 r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
302 if (r >= 0) {
2c154c54 303 r = ordered_set_put_strdupv_full(s, &dns_name_hash_ops_free, domains);
86a66e9b
YW
304 if (r < 0)
305 return r;
306 }
307 }
308
7a169cb4 309 if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) {
86a66e9b
YW
310 char **domains;
311
312 r = sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains);
313 if (r >= 0) {
2c154c54 314 r = ordered_set_put_strdupv_full(s, &dns_name_hash_ops_free, domains);
86a66e9b
YW
315 if (r < 0)
316 return r;
317 }
318 }
319
7a169cb4 320 if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) {
86a66e9b
YW
321 NDiscDNSSL *a;
322
323 SET_FOREACH(a, link->ndisc_dnssl) {
2c154c54 324 r = ordered_set_put_strdup_full(s, &dns_name_hash_ops_free, ndisc_dnssl_domain(a));
86a66e9b
YW
325 if (r < 0)
326 return r;
327 }
3b5a4fc6
YW
328 }
329
86a66e9b 330 return 0;
3b5a4fc6
YW
331}
332
333int manager_save(Manager *m) {
cf453507 334 _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
bcdcc596 335 const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str, *online_state_str;
3b5a4fc6
YW
336 LinkOperationalState operstate = LINK_OPERSTATE_OFF;
337 LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
8430841b
L
338 LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF,
339 address_state = LINK_ADDRESS_STATE_OFF;
bcdcc596
AÅ 
340 LinkOnlineState online_state;
341 size_t links_offline = 0, links_online = 0;
3b5a4fc6
YW
342 int r;
343
344 assert(m);
4c78dc17
YW
345
346 if (isempty(m->state_file))
347 return 0; /* Do not update state file when running in test mode. */
3b5a4fc6 348
4c8dc664 349 Link *link;
6eab614d 350 HASHMAP_FOREACH(link, m->links_by_index) {
3b5a4fc6
YW
351 if (link->flags & IFF_LOOPBACK)
352 continue;
353
90afec18
YW
354 operstate = MAX(operstate, link->operstate);
355 carrier_state = MAX(carrier_state, link->carrier_state);
356 address_state = MAX(address_state, link->address_state);
357 ipv4_address_state = MAX(ipv4_address_state, link->ipv4_address_state);
358 ipv6_address_state = MAX(ipv6_address_state, link->ipv6_address_state);
8430841b 359
3b5a4fc6
YW
360 if (!link->network)
361 continue;
362
bcdcc596
AÅ 
363 if (link->network->required_for_online) {
364 if (link->online_state == LINK_ONLINE_STATE_OFFLINE)
365 links_offline++;
366 else if (link->online_state == LINK_ONLINE_STATE_ONLINE)
367 links_online++;
368 }
369
86a66e9b 370 r = link_put_dns(link, &dns);
3b5a4fc6
YW
371 if (r < 0)
372 return r;
373
86a66e9b 374 r = link_put_ntp(link, &ntp);
3b5a4fc6
YW
375 if (r < 0)
376 return r;
377
86a66e9b 378 r = link_put_sip(link, &sip);
3b5a4fc6
YW
379 if (r < 0)
380 return r;
381
86a66e9b 382 r = link_put_domains(link, /* is_route = */ false, &search_domains);
3b5a4fc6
YW
383 if (r < 0)
384 return r;
385
86a66e9b
YW
386 r = link_put_domains(link, /* is_route = */ true, &route_domains);
387 if (r < 0)
388 return r;
3b5a4fc6
YW
389 }
390
391 if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
392 carrier_state = LINK_CARRIER_STATE_CARRIER;
393
bcdcc596
AÅ 
394 online_state = links_online > 0 ?
395 (links_offline > 0 ? LINK_ONLINE_STATE_PARTIAL : LINK_ONLINE_STATE_ONLINE) :
396 (links_offline > 0 ? LINK_ONLINE_STATE_OFFLINE : _LINK_ONLINE_STATE_INVALID);
397
4c8dc664
MY
398 operstate_str = ASSERT_PTR(link_operstate_to_string(operstate));
399 carrier_state_str = ASSERT_PTR(link_carrier_state_to_string(carrier_state));
400 address_state_str = ASSERT_PTR(link_address_state_to_string(address_state));
401 ipv4_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv4_address_state));
402 ipv6_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv6_address_state));
3b5a4fc6 403
4c8dc664
MY
404 _cleanup_(unlink_and_freep) char *temp_path = NULL;
405 _cleanup_fclose_ FILE *f = NULL;
8430841b 406
3b5a4fc6
YW
407 r = fopen_temporary(m->state_file, &f, &temp_path);
408 if (r < 0)
409 return r;
410
411 (void) fchmod(fileno(f), 0644);
412
413 fprintf(f,
414 "# This is private data. Do not parse.\n"
415 "OPER_STATE=%s\n"
416 "CARRIER_STATE=%s\n"
8430841b
L
417 "ADDRESS_STATE=%s\n"
418 "IPV4_ADDRESS_STATE=%s\n"
419 "IPV6_ADDRESS_STATE=%s\n",
420 operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str);
3b5a4fc6 421
bcdcc596
AÅ 
422 online_state_str = link_online_state_to_string(online_state);
423 if (online_state_str)
424 fprintf(f, "ONLINE_STATE=%s\n", online_state_str);
425
3b5a4fc6
YW
426 ordered_set_print(f, "DNS=", dns);
427 ordered_set_print(f, "NTP=", ntp);
428 ordered_set_print(f, "SIP=", sip);
429 ordered_set_print(f, "DOMAINS=", search_domains);
430 ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
431
432 r = fflush_and_check(f);
433 if (r < 0)
d23a66f2 434 return r;
3b5a4fc6
YW
435
436 r = conservative_rename(temp_path, m->state_file);
437 if (r < 0)
d23a66f2
YW
438 return r;
439
440 temp_path = mfree(temp_path);
3b5a4fc6 441
4c8dc664
MY
442 _cleanup_strv_free_ char **p = NULL;
443
3b5a4fc6
YW
444 if (m->operational_state != operstate) {
445 m->operational_state = operstate;
446 if (strv_extend(&p, "OperationalState") < 0)
447 log_oom();
448 }
449
450 if (m->carrier_state != carrier_state) {
451 m->carrier_state = carrier_state;
452 if (strv_extend(&p, "CarrierState") < 0)
453 log_oom();
454 }
455
456 if (m->address_state != address_state) {
457 m->address_state = address_state;
458 if (strv_extend(&p, "AddressState") < 0)
459 log_oom();
460 }
461
8430841b
L
462 if (m->ipv4_address_state != ipv4_address_state) {
463 m->ipv4_address_state = ipv4_address_state;
464 if (strv_extend(&p, "IPv4AddressState") < 0)
465 log_oom();
466 }
467
468 if (m->ipv6_address_state != ipv6_address_state) {
469 m->ipv6_address_state = ipv6_address_state;
470 if (strv_extend(&p, "IPv6AddressState") < 0)
471 log_oom();
472 }
473
bcdcc596
AÅ 
474 if (m->online_state != online_state) {
475 m->online_state = online_state;
476 if (strv_extend(&p, "OnlineState") < 0)
477 log_oom();
478 }
479
3b5a4fc6
YW
480 if (p) {
481 r = manager_send_changed_strv(m, p);
482 if (r < 0)
d23a66f2 483 log_warning_errno(r, "Could not emit changed properties, ignoring: %m");
3b5a4fc6
YW
484 }
485
486 m->dirty = false;
487
488 return 0;
3b5a4fc6
YW
489}
490
491static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
492 bool space = false;
493 Link *link;
494
495 assert(f);
496 assert(prefix);
497
498 if (hashmap_isempty(h))
499 return;
500
501 fputs(prefix, f);
502 HASHMAP_FOREACH(link, h) {
503 if (space)
504 fputc(' ', f);
505
506 fprintf(f, "%i", link->ifindex);
507 space = true;
508 }
509
510 fputc('\n', f);
511}
512
513static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
56437e33
YW
514 bool _space = false;
515
516 if (!space)
517 space = &_space;
518
3b5a4fc6
YW
519 for (unsigned j = 0; j < n_dns; j++) {
520 const char *str;
521
522 if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
523 continue;
524
525 str = in_addr_full_to_string(dns[j]);
526 if (!str)
527 continue;
528
529 if (*space)
530 fputc(' ', f);
531 fputs(str, f);
532 *space = true;
533 }
534}
535
536static void serialize_addresses(
537 FILE *f,
538 const char *lvalue,
539 bool *space,
540 char **addresses,
541 sd_dhcp_lease *lease,
542 bool conditional,
543 sd_dhcp_lease_server_type_t what,
544 sd_dhcp6_lease *lease6,
545 bool conditional6,
546 int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
547 int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
3b5a4fc6
YW
548
549 bool _space = false;
44e1f7e3
YW
550 int r;
551
3b5a4fc6
YW
552 if (!space)
553 space = &_space;
554
555 if (lvalue)
556 fprintf(f, "%s=", lvalue);
557 fputstrv(f, addresses, NULL, space);
558
559 if (lease && conditional) {
560 const struct in_addr *lease_addresses;
561
562 r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
563 if (r > 0)
564 serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
565 }
566
567 if (lease6 && conditional6 && lease6_get_addr) {
568 const struct in6_addr *in6_addrs;
569
570 r = lease6_get_addr(lease6, &in6_addrs);
571 if (r > 0)
572 serialize_in6_addrs(f, in6_addrs, r, space);
573 }
574
575 if (lease6 && conditional6 && lease6_get_fqdn) {
576 char **in6_hosts;
577
578 r = lease6_get_fqdn(lease6, &in6_hosts);
579 if (r > 0)
580 fputstrv(f, in6_hosts, NULL, space);
581 }
582
583 if (lvalue)
584 fputc('\n', f);
585}
586
3fd6708c
RP
587static void serialize_resolvers(
588 FILE *f,
589 const char *lvalue,
590 bool *space,
591 sd_dhcp_lease *lease,
168ad243
RP
592 bool conditional,
593 sd_dhcp6_lease *lease6,
594 bool conditional6) {
3fd6708c
RP
595
596 bool _space = false;
597 if (!space)
598 space = &_space;
599
600 if (lvalue)
601 fprintf(f, "%s=", lvalue);
602
603 if (lease && conditional) {
604 sd_dns_resolver *resolvers;
605 _cleanup_strv_free_ char **names = NULL;
606 int r;
607
608 r = sd_dhcp_lease_get_dnr(lease, &resolvers);
57feaaec
RP
609 if (r < 0 && r != -ENODATA)
610 log_warning_errno(r, "Failed to get DNR from DHCP lease, ignoring: %m");
3fd6708c 611
57feaaec
RP
612 if (r > 0) {
613 r = dns_resolvers_to_dot_strv(resolvers, r, &names);
614 if (r < 0)
615 return (void) log_warning_errno(r, "Failed to get DoT servers from DHCP DNR, ignoring: %m");
616 if (r > 0)
617 fputstrv(f, names, NULL, space);
618 }
3fd6708c
RP
619 }
620
168ad243
RP
621 if (lease6 && conditional6) {
622 sd_dns_resolver *resolvers;
623 _cleanup_strv_free_ char **names = NULL;
624 int r;
625
626 r = sd_dhcp6_lease_get_dnr(lease6, &resolvers);
57feaaec
RP
627 if (r < 0 && r != -ENODATA)
628 log_warning_errno(r, "Failed to get DNR from DHCPv6 lease, ignoring: %m");
168ad243 629
57feaaec
RP
630 if (r > 0) {
631 r = dns_resolvers_to_dot_strv(resolvers, r, &names);
632 if (r < 0)
633 return (void) log_warning_errno(r, "Failed to get DoT servers from DHCPv6 DNR, ignoring: %m");
634 if (r > 0)
635 fputstrv(f, names, NULL, space);
636 }
168ad243
RP
637 }
638
3fd6708c
RP
639 if (lvalue)
640 fputc('\n', f);
641
642 return;
643}
644
78f5c649 645static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, UseDomains use_domains) {
56437e33
YW
646 bool space = false;
647 const char *p;
648
649 assert(link);
650 assert(link->network);
651 assert(f);
652
653 ORDERED_SET_FOREACH(p, static_domains)
215286a4 654 fputs_with_separator(f, p, NULL, &space);
56437e33 655
78f5c649 656 if (use_domains == USE_DOMAINS_NO)
56437e33
YW
657 return;
658
7a169cb4 659 if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) {
56437e33
YW
660 const char *domainname;
661 char **domains;
662
663 if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname) >= 0)
215286a4 664 fputs_with_separator(f, domainname, NULL, &space);
56437e33
YW
665 if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0)
666 fputstrv(f, domains, NULL, &space);
667 }
668
7a169cb4 669 if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) {
56437e33
YW
670 char **domains;
671
672 if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0)
673 fputstrv(f, domains, NULL, &space);
674 }
675
7a169cb4 676 if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) {
56437e33
YW
677 NDiscDNSSL *dd;
678
679 SET_FOREACH(dd, link->ndisc_dnssl)
baa3fadf 680 fputs_with_separator(f, ndisc_dnssl_domain(dd), NULL, &space);
56437e33
YW
681 }
682}
683
2cf9b1a0
MY
684static int serialize_config_files(FILE *f, const char *prefix, const char *main_config, char * const *dropins) {
685 assert(f);
686 assert(prefix);
687 assert(main_config);
688
689 fprintf(f, "%s_FILE=%s\n", prefix, main_config);
690
691 bool space = false;
692
693 fprintf(f, "%s_FILE_DROPINS=\"", prefix);
694 STRV_FOREACH(d, dropins) {
695 _cleanup_free_ char *escaped = NULL;
696
697 escaped = xescape(*d, ":");
698 if (!escaped)
699 return -ENOMEM;
700
701 fputs_with_separator(f, escaped, ":", &space);
702 }
703 fputs("\"\n", f);
704
705 return 0;
706}
707
a70c27b8 708static int link_save(Link *link) {
4c8dc664 709 const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
d23a66f2 710 _cleanup_(unlink_and_freep) char *temp_path = NULL;
3b5a4fc6
YW
711 _cleanup_fclose_ FILE *f = NULL;
712 int r;
713
714 assert(link);
3b5a4fc6
YW
715 assert(link->manager);
716
4c78dc17
YW
717 if (isempty(link->state_file))
718 return 0; /* Do not update state files when running in test mode. */
719
d23a66f2 720 if (link->state == LINK_STATE_LINGER)
3b5a4fc6 721 return 0;
3b5a4fc6 722
4c8dc664
MY
723 admin_state = ASSERT_PTR(link_state_to_string(link->state));
724 oper_state = ASSERT_PTR(link_operstate_to_string(link->operstate));
725 carrier_state = ASSERT_PTR(link_carrier_state_to_string(link->carrier_state));
726 address_state = ASSERT_PTR(link_address_state_to_string(link->address_state));
727 ipv4_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv4_address_state));
728 ipv6_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv6_address_state));
8430841b 729
3b5a4fc6
YW
730 r = fopen_temporary(link->state_file, &f, &temp_path);
731 if (r < 0)
d23a66f2 732 return r;
3b5a4fc6
YW
733
734 (void) fchmod(fileno(f), 0644);
735
736 fprintf(f,
737 "# This is private data. Do not parse.\n"
738 "ADMIN_STATE=%s\n"
739 "OPER_STATE=%s\n"
740 "CARRIER_STATE=%s\n"
8430841b
L
741 "ADDRESS_STATE=%s\n"
742 "IPV4_ADDRESS_STATE=%s\n"
743 "IPV6_ADDRESS_STATE=%s\n",
744 admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
3b5a4fc6 745
f49c8e65
YW
746 if (link->netdev) {
747 r = serialize_config_files(f, "NETDEV", link->netdev->filename, link->netdev->dropins);
748 if (r < 0)
749 return r;
750 }
751
3b5a4fc6 752 if (link->network) {
4c8dc664 753 const char *online_state, *captive_portal;
a2640646 754 bool space = false;
3b5a4fc6 755
bcdcc596
AÅ 
756 online_state = link_online_state_to_string(link->online_state);
757 if (online_state)
758 fprintf(f, "ONLINE_STATE=%s\n", online_state);
759
3b5a4fc6
YW
760 fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
761 yes_no(link->network->required_for_online));
762
2278d9f6
YW
763 LinkOperationalStateRange st;
764 link_required_operstate_for_online(link, &st);
765
766 fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s:%s\n",
767 link_operstate_to_string(st.min), link_operstate_to_string(st.max));
3b5a4fc6 768
8430841b 769 fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
a853cc99 770 link_required_address_family_to_string(link_required_family_for_online(link)));
8430841b 771
3b5a4fc6
YW
772 fprintf(f, "ACTIVATION_POLICY=%s\n",
773 activation_policy_to_string(link->network->activation_policy));
774
2cf9b1a0
MY
775 r = serialize_config_files(f, "NETWORK", link->network->filename, link->network->dropins);
776 if (r < 0)
777 return r;
a2640646 778
3b5a4fc6
YW
779 /************************************************************/
780
56437e33
YW
781 fputs("DNS=", f);
782 if (link->n_dns != UINT_MAX)
783 link_save_dns(link, f, link->dns, link->n_dns, NULL);
784 else {
785 space = false;
786 link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
3b5a4fc6 787
3fd6708c
RP
788 /* DNR resolvers are not required to provide Do53 service, however resolved doesn't
789 * know how to handle such a server so for now Do53 service is required, and
790 * assumed. */
791 serialize_resolvers(f, NULL, &space,
792 link->dhcp_lease,
168ad243
RP
793 link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4),
794 link->dhcp6_lease,
795 link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6));
3fd6708c 796
65187c46
RP
797 if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) {
798 NDiscDNR *dnr;
799 SET_FOREACH(dnr, link->ndisc_dnr)
800 serialize_dnr(f, &dnr->resolver, 1, &space);
801 }
802
56437e33
YW
803 serialize_addresses(f, NULL, &space,
804 NULL,
805 link->dhcp_lease,
9646ffe2 806 link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4),
56437e33
YW
807 SD_DHCP_LEASE_DNS,
808 link->dhcp6_lease,
9646ffe2 809 link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6),
56437e33
YW
810 sd_dhcp6_lease_get_dns,
811 NULL);
812
9646ffe2 813 if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
56437e33
YW
814 NDiscRDNSS *dd;
815
816 SET_FOREACH(dd, link->ndisc_rdnss)
817 serialize_in6_addrs(f, &dd->address, 1, &space);
818 }
3b5a4fc6
YW
819 }
820
821 fputc('\n', f);
822
823 /************************************************************/
824
56437e33
YW
825 if (link->ntp) {
826 fputs("NTP=", f);
827 fputstrv(f, link->ntp, NULL, NULL);
828 fputc('\n', f);
829 } else
830 serialize_addresses(f, "NTP", NULL,
831 link->network->ntp,
832 link->dhcp_lease,
d12fb2bc 833 link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4),
56437e33
YW
834 SD_DHCP_LEASE_NTP,
835 link->dhcp6_lease,
d12fb2bc 836 link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6),
56437e33
YW
837 sd_dhcp6_lease_get_ntp_addrs,
838 sd_dhcp6_lease_get_ntp_fqdn);
3b5a4fc6
YW
839
840 serialize_addresses(f, "SIP", NULL,
841 NULL,
842 link->dhcp_lease,
843 link->network->dhcp_use_sip,
844 SD_DHCP_LEASE_SIP,
845 NULL, false, NULL, NULL);
846
847 /************************************************************/
848
6341ea54
YW
849 r = link_get_captive_portal(link, &captive_portal);
850 if (r < 0)
851 return r;
a75feb55 852
6341ea54
YW
853 if (captive_portal)
854 fprintf(f, "CAPTIVE_PORTAL=%s\n", captive_portal);
edb88a72
RP
855
856 /************************************************************/
857
3b5a4fc6 858 fputs("DOMAINS=", f);
56437e33 859 if (link->search_domains)
78f5c649 860 link_save_domains(link, f, link->search_domains, USE_DOMAINS_NO);
56437e33 861 else
78f5c649 862 link_save_domains(link, f, link->network->search_domains, USE_DOMAINS_YES);
3b5a4fc6
YW
863 fputc('\n', f);
864
865 /************************************************************/
866
867 fputs("ROUTE_DOMAINS=", f);
56437e33 868 if (link->route_domains)
78f5c649 869 link_save_domains(link, f, link->route_domains, USE_DOMAINS_NO);
56437e33 870 else
78f5c649 871 link_save_domains(link, f, link->network->route_domains, USE_DOMAINS_ROUTE);
3b5a4fc6
YW
872 fputc('\n', f);
873
874 /************************************************************/
875
876 fprintf(f, "LLMNR=%s\n",
877 resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
878
879 /************************************************************/
880
881 fprintf(f, "MDNS=%s\n",
882 resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
883
884 /************************************************************/
885
886 int dns_default_route =
887 link->dns_default_route >= 0 ? link->dns_default_route :
888 link->network->dns_default_route;
889 if (dns_default_route >= 0)
890 fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
891
892 /************************************************************/
893
894 DnsOverTlsMode dns_over_tls_mode =
895 link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
896 link->network->dns_over_tls_mode;
897 if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
898 fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
899
900 /************************************************************/
901
902 DnssecMode dnssec_mode =
903 link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
904 link->network->dnssec_mode;
905 if (dnssec_mode != _DNSSEC_MODE_INVALID)
906 fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
907
908 /************************************************************/
909
910 Set *nta_anchors = link->dnssec_negative_trust_anchors;
911 if (set_isempty(nta_anchors))
912 nta_anchors = link->network->dnssec_negative_trust_anchors;
913
914 if (!set_isempty(nta_anchors)) {
915 const char *n;
916
3b5a4fc6 917 space = false;
2cf9b1a0
MY
918
919 fputs("DNSSEC_NTA=", f);
3b5a4fc6 920 SET_FOREACH(n, nta_anchors)
215286a4 921 fputs_with_separator(f, n, NULL, &space);
3b5a4fc6
YW
922 fputc('\n', f);
923 }
924 }
925
926 print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
927 print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
928
929 if (link->dhcp_lease) {
930 r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
931 if (r < 0)
d23a66f2 932 return r;
3b5a4fc6 933
2cf9b1a0 934 fprintf(f, "DHCP_LEASE=%s\n", link->lease_file);
3b5a4fc6
YW
935 } else
936 (void) unlink(link->lease_file);
937
938 r = link_serialize_dhcp6_client(link, f);
939 if (r < 0)
d23a66f2 940 return r;
3b5a4fc6
YW
941
942 r = fflush_and_check(f);
943 if (r < 0)
d23a66f2 944 return r;
3b5a4fc6
YW
945
946 r = conservative_rename(temp_path, link->state_file);
947 if (r < 0)
d23a66f2 948 return r;
3b5a4fc6 949
d23a66f2 950 temp_path = mfree(temp_path);
3b5a4fc6 951
d23a66f2 952 return 0;
3b5a4fc6
YW
953}
954
955void link_dirty(Link *link) {
3b5a4fc6
YW
956 assert(link);
957 assert(link->manager);
958
ef45f5c8
YW
959 /* When the manager is in MANAGER_STOPPED, it is not necessary to update state files anymore, as they
960 * will be removed soon anyway. Moreover, we cannot call link_ref() in that case. */
961 if (link->manager->state == MANAGER_STOPPED)
962 return;
963
3b5a4fc6
YW
964 /* The serialized state in /run is no longer up-to-date. */
965
966 /* Also mark manager dirty as link is dirty */
967 link->manager->dirty = true;
968
4cf443e6
YW
969 if (set_ensure_put(&link->manager->dirty_links, &link_hash_ops, link) <= 0)
970 return; /* Ignore allocation errors and don't take another ref if the link was already dirty */
971
3b5a4fc6
YW
972 link_ref(link);
973}
974
975void link_clean(Link *link) {
976 assert(link);
977 assert(link->manager);
978
979 /* The serialized state in /run is up-to-date */
980
981 link_unref(set_remove(link->manager->dirty_links, link));
982}
983
bd7d9028
YW
984int link_save_and_clean_full(Link *link, bool also_save_manager) {
985 int r, k = 0;
986
987 assert(link);
988 assert(link->manager);
989
990 if (also_save_manager)
991 k = manager_save(link->manager);
3b5a4fc6
YW
992
993 r = link_save(link);
994 if (r < 0)
995 return r;
996
997 link_clean(link);
bd7d9028 998 return k;
3b5a4fc6 999}
2e1113b7
YW
1000
1001int manager_clean_all(Manager *manager) {
1002 int r, ret = 0;
1003
1004 assert(manager);
1005
1006 if (manager->dirty) {
1007 r = manager_save(manager);
1008 if (r < 0)
1009 log_warning_errno(r, "Failed to update state file %s, ignoring: %m", manager->state_file);
1010 RET_GATHER(ret, r);
1011 }
1012
1013 Link *link;
1014 SET_FOREACH(link, manager->dirty_links) {
1015 r = link_save_and_clean(link);
1016 if (r < 0)
1017 log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file);
1018 RET_GATHER(ret, r);
1019 }
1020
1021 return ret;
1022}