]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/generator/network-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "ether-addr-util.h"
6 #include "hostname-util.h"
9 #include "network-generator.h"
10 #include "parse-util.h"
11 #include "proc-cmdline.h"
12 #include "socket-util.h"
13 #include "string-table.h"
14 #include "string-util.h"
19 ip={dhcp|on|any|dhcp6|auto6|either6}
20 ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]]
21 ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>]]
22 ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]]
23 rd.route=<net>/<netmask>:<gateway>[:<interface>]
24 nameserver=<IP> [nameserver=<IP> ...]
28 ifname=<interface>:<MAC>
31 vlan=<vlanname>:<phydevice>
32 bond=<bondname>[:<bondslaves>:[:<options>[:<mtu>]]]
33 team=<teammaster>:<teamslaves> # not supported
34 bridge=<bridgename>:<ethnames>
44 static const char * const dracut_dhcp_type_table
[_DHCP_TYPE_MAX
] = {
45 [DHCP_TYPE_NONE
] = "none",
46 [DHCP_TYPE_OFF
] = "off",
47 [DHCP_TYPE_ON
] = "on",
48 [DHCP_TYPE_ANY
] = "any",
49 [DHCP_TYPE_DHCP
] = "dhcp",
50 [DHCP_TYPE_DHCP6
] = "dhcp6",
51 [DHCP_TYPE_AUTO6
] = "auto6",
52 [DHCP_TYPE_EITHER6
] = "either6",
53 [DHCP_TYPE_IBFT
] = "ibft",
56 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dracut_dhcp_type
, DHCPType
);
58 static const char * const networkd_dhcp_type_table
[_DHCP_TYPE_MAX
] = {
59 [DHCP_TYPE_NONE
] = "no",
60 [DHCP_TYPE_OFF
] = "no",
61 [DHCP_TYPE_ON
] = "yes",
62 [DHCP_TYPE_ANY
] = "yes",
63 [DHCP_TYPE_DHCP
] = "ipv4",
64 [DHCP_TYPE_DHCP6
] = "ipv6",
65 [DHCP_TYPE_AUTO6
] = "no", /* TODO: enable other setting? */
66 [DHCP_TYPE_EITHER6
] = "ipv6", /* TODO: enable other setting? */
67 [DHCP_TYPE_IBFT
] = "no",
70 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(networkd_dhcp_type
, DHCPType
);
72 static Address
*address_free(Address
*address
) {
77 LIST_REMOVE(addresses
, address
->network
->addresses
, address
);
79 return mfree(address
);
82 static int address_new(Network
*network
, int family
, unsigned char prefixlen
,
83 union in_addr_union
*addr
, union in_addr_union
*peer
, Address
**ret
) {
88 address
= new(Address
, 1);
92 *address
= (Address
) {
94 .prefixlen
= prefixlen
,
99 LIST_PREPEND(addresses
, network
->addresses
, address
);
101 address
->network
= network
;
108 static Route
*route_free(Route
*route
) {
113 LIST_REMOVE(routes
, route
->network
->routes
, route
);
118 static int route_new(Network
*network
, int family
, unsigned char prefixlen
,
119 union in_addr_union
*dest
, union in_addr_union
*gateway
, Route
**ret
) {
124 route
= new(Route
, 1);
130 .prefixlen
= prefixlen
,
131 .dest
= dest
? *dest
: IN_ADDR_NULL
,
135 LIST_PREPEND(routes
, network
->routes
, route
);
137 route
->network
= network
;
144 static Network
*network_free(Network
*network
) {
151 free(network
->ifname
);
152 free(network
->hostname
);
153 strv_free(network
->dns
);
155 free(network
->bridge
);
158 while ((address
= network
->addresses
))
159 address_free(address
);
161 while ((route
= network
->routes
))
164 return mfree(network
);
167 DEFINE_TRIVIAL_CLEANUP_FUNC(Network
*, network_free
);
169 static int network_new(Context
*context
, const char *name
, Network
**ret
) {
170 _cleanup_(network_freep
) Network
*network
= NULL
;
171 _cleanup_free_
char *ifname
= NULL
;
176 if (!isempty(name
) && !ifname_valid(name
))
179 ifname
= strdup(name
);
183 network
= new(Network
, 1);
187 *network
= (Network
) {
188 .ifname
= TAKE_PTR(ifname
),
189 .dhcp_type
= _DHCP_TYPE_INVALID
,
193 r
= hashmap_ensure_put(&context
->networks_by_name
, &string_hash_ops
, network
->ifname
, network
);
204 Network
*network_get(Context
*context
, const char *ifname
) {
205 return hashmap_get(context
->networks_by_name
, ifname
);
208 static NetDev
*netdev_free(NetDev
*netdev
) {
212 free(netdev
->ifname
);
214 return mfree(netdev
);
217 DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev
*, netdev_free
);
219 static int netdev_new(Context
*context
, const char *_kind
, const char *_ifname
, NetDev
**ret
) {
220 _cleanup_(netdev_freep
) NetDev
*netdev
= NULL
;
221 _cleanup_free_
char *kind
= NULL
, *ifname
= NULL
;
226 if (!ifname_valid(_ifname
))
229 kind
= strdup(_kind
);
233 ifname
= strdup(_ifname
);
237 netdev
= new(NetDev
, 1);
242 .kind
= TAKE_PTR(kind
),
243 .ifname
= TAKE_PTR(ifname
),
246 r
= hashmap_ensure_put(&context
->netdevs_by_name
, &string_hash_ops
, netdev
->ifname
, netdev
);
257 NetDev
*netdev_get(Context
*context
, const char *ifname
) {
258 return hashmap_get(context
->netdevs_by_name
, ifname
);
261 static Link
*link_free(Link
*link
) {
269 DEFINE_TRIVIAL_CLEANUP_FUNC(Link
*, link_free
);
271 static int link_new(Context
*context
, const char *name
, struct ether_addr
*mac
, Link
**ret
) {
272 _cleanup_(link_freep
) Link
*link
= NULL
;
273 _cleanup_free_
char *ifname
= NULL
;
278 if (!ifname_valid(name
))
281 ifname
= strdup(name
);
290 .ifname
= TAKE_PTR(ifname
),
294 r
= hashmap_ensure_put(&context
->links_by_name
, &string_hash_ops
, link
->ifname
, link
);
305 Link
*link_get(Context
*context
, const char *ifname
) {
306 return hashmap_get(context
->links_by_name
, ifname
);
309 static int network_set_dhcp_type(Context
*context
, const char *ifname
, const char *dhcp_type
) {
314 t
= dracut_dhcp_type_from_string(dhcp_type
);
318 network
= network_get(context
, ifname
);
320 r
= network_new(context
, ifname
, &network
);
325 network
->dhcp_type
= t
;
329 static int network_set_hostname(Context
*context
, const char *ifname
, const char *hostname
) {
332 network
= network_get(context
, ifname
);
336 return free_and_strdup(&network
->hostname
, hostname
);
339 static int network_set_mtu(Context
*context
, const char *ifname
, int family
, const char *mtu
) {
342 network
= network_get(context
, ifname
);
346 return parse_mtu(family
, mtu
, &network
->mtu
);
349 static int network_set_mac_address(Context
*context
, const char *ifname
, const char *mac
) {
352 network
= network_get(context
, ifname
);
356 return ether_addr_from_string(mac
, &network
->mac
);
359 static int network_set_address(Context
*context
, const char *ifname
, int family
, unsigned char prefixlen
,
360 union in_addr_union
*addr
, union in_addr_union
*peer
) {
363 if (in_addr_is_null(family
, addr
) != 0)
366 network
= network_get(context
, ifname
);
370 return address_new(network
, family
, prefixlen
, addr
, peer
, NULL
);
373 static int network_set_route(Context
*context
, const char *ifname
, int family
, unsigned char prefixlen
,
374 union in_addr_union
*dest
, union in_addr_union
*gateway
) {
378 if (in_addr_is_null(family
, gateway
) != 0)
381 network
= network_get(context
, ifname
);
383 r
= network_new(context
, ifname
, &network
);
388 return route_new(network
, family
, prefixlen
, dest
, gateway
, NULL
);
391 static int network_set_dns(Context
*context
, const char *ifname
, const char *dns
) {
392 union in_addr_union a
;
396 r
= in_addr_from_string_auto(dns
, &family
, &a
);
400 network
= network_get(context
, ifname
);
402 r
= network_new(context
, ifname
, &network
);
407 return strv_extend(&network
->dns
, dns
);
410 static int network_set_dhcp_use_dns(Context
*context
, const char *ifname
, bool value
) {
414 network
= network_get(context
, ifname
);
416 r
= network_new(context
, ifname
, &network
);
421 network
->dhcp_use_dns
= value
;
426 static int network_set_vlan(Context
*context
, const char *ifname
, const char *value
) {
430 network
= network_get(context
, ifname
);
432 r
= network_new(context
, ifname
, &network
);
437 return free_and_strdup(&network
->vlan
, value
);
440 static int network_set_bridge(Context
*context
, const char *ifname
, const char *value
) {
444 network
= network_get(context
, ifname
);
446 r
= network_new(context
, ifname
, &network
);
451 return free_and_strdup(&network
->bridge
, value
);
454 static int network_set_bond(Context
*context
, const char *ifname
, const char *value
) {
458 network
= network_get(context
, ifname
);
460 r
= network_new(context
, ifname
, &network
);
465 return free_and_strdup(&network
->bond
, value
);
468 static int parse_cmdline_ip_mtu_mac(Context
*context
, const char *ifname
, int family
, const char *value
) {
472 /* [<mtu>][:<macaddr>] */
474 p
= strchr(value
, ':');
478 mtu
= strndupa(value
, p
- value
);
480 r
= network_set_mtu(context
, ifname
, family
, mtu
);
487 r
= network_set_mac_address(context
, ifname
, p
+ 1);
494 static int parse_ip_address_one(int family
, const char **value
, union in_addr_union
*ret
) {
495 const char *p
= *value
, *q
, *buf
;
503 if (family
== AF_INET6
) {
507 q
= strchr(p
+ 1, ']');
514 buf
= strndupa(p
+ 1, q
- p
- 1);
521 buf
= strndupa(p
, q
- p
);
525 r
= in_addr_from_string(family
, buf
, ret
);
533 static int parse_netmask_or_prefixlen(int family
, const char **value
, unsigned char *ret
) {
534 union in_addr_union netmask
;
538 r
= parse_ip_address_one(family
, value
, &netmask
);
540 if (family
== AF_INET6
)
541 /* TODO: Not supported yet. */
544 *ret
= in4_addr_netmask_to_prefixlen(&netmask
.in
);
546 *ret
= family
== AF_INET6
? 128 : 32;
548 p
= strchr(*value
, ':');
552 q
= strndupa(*value
, p
- *value
);
553 r
= safe_atou8(q
, ret
);
563 static int parse_cmdline_ip_address(Context
*context
, int family
, const char *value
) {
564 union in_addr_union addr
= {}, peer
= {}, gateway
= {};
565 const char *hostname
= NULL
, *ifname
, *dhcp_type
, *dns
, *p
;
566 unsigned char prefixlen
;
569 /* ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>]]
570 * ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]] */
572 r
= parse_ip_address_one(family
, &value
, &addr
);
575 r
= parse_ip_address_one(family
, &value
, &peer
);
578 r
= parse_ip_address_one(family
, &value
, &gateway
);
581 r
= parse_netmask_or_prefixlen(family
, &value
, &prefixlen
);
586 p
= strchr(value
, ':');
591 hostname
= strndupa(value
, p
- value
);
592 if (!hostname_is_valid(hostname
, 0))
599 p
= strchr(value
, ':');
603 ifname
= strndupa(value
, p
- value
);
608 p
= strchr(value
, ':');
612 dhcp_type
= strndupa(value
, p
- value
);
614 r
= network_set_dhcp_type(context
, ifname
, dhcp_type
);
619 r
= network_set_hostname(context
, ifname
, hostname
);
623 r
= network_set_address(context
, ifname
, family
, prefixlen
, &addr
, &peer
);
627 r
= network_set_route(context
, ifname
, family
, 0, NULL
, &gateway
);
634 /* First, try [<mtu>][:<macaddr>] */
635 r
= parse_cmdline_ip_mtu_mac(context
, ifname
, AF_UNSPEC
, p
+ 1);
639 /* Next, try [<dns1>][:<dns2>] */
641 p
= strchr(value
, ':');
643 r
= network_set_dns(context
, ifname
, value
);
647 dns
= strndupa(value
, p
- value
);
648 r
= network_set_dns(context
, ifname
, dns
);
651 r
= network_set_dns(context
, ifname
, p
+ 1);
659 static int parse_cmdline_ip_interface(Context
*context
, const char *value
) {
660 const char *ifname
, *dhcp_type
, *p
;
663 /* ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]] */
665 p
= strchr(value
, ':');
669 ifname
= strndupa(value
, p
- value
);
672 p
= strchr(value
, ':');
676 dhcp_type
= strndupa(value
, p
- value
);
678 r
= network_set_dhcp_type(context
, ifname
, dhcp_type
);
685 return parse_cmdline_ip_mtu_mac(context
, ifname
, AF_UNSPEC
, p
+ 1);
688 static int parse_cmdline_ip(Context
*context
, const char *key
, const char *value
) {
692 if (proc_cmdline_value_missing(key
, value
))
695 p
= strchr(value
, ':');
697 /* ip={dhcp|on|any|dhcp6|auto6|either6} */
698 return network_set_dhcp_type(context
, "", value
);
701 return parse_cmdline_ip_address(context
, AF_INET6
, value
);
703 r
= parse_cmdline_ip_address(context
, AF_INET
, value
);
705 return parse_cmdline_ip_interface(context
, value
);
710 static int parse_cmdline_rd_route(Context
*context
, const char *key
, const char *value
) {
711 union in_addr_union addr
= {}, gateway
= {};
712 unsigned char prefixlen
;
716 /* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
718 if (proc_cmdline_value_missing(key
, value
))
721 if (value
[0] == '[') {
722 p
= strchr(value
, ']');
729 buf
= strndupa(value
+ 1, p
- value
- 1);
733 p
= strchr(value
, ':');
737 buf
= strndupa(value
, p
- value
);
742 r
= in_addr_prefix_from_string(buf
, family
, &addr
, &prefixlen
);
746 p
= strchr(value
, ':');
748 value
= strjoina(value
, ":");
750 r
= parse_ip_address_one(family
, &value
, &gateway
);
754 return network_set_route(context
, value
, family
, prefixlen
, &addr
, &gateway
);
757 static int parse_cmdline_nameserver(Context
*context
, const char *key
, const char *value
) {
758 if (proc_cmdline_value_missing(key
, value
))
761 return network_set_dns(context
, "", value
);
764 static int parse_cmdline_rd_peerdns(Context
*context
, const char *key
, const char *value
) {
767 if (proc_cmdline_value_missing(key
, value
))
768 return network_set_dhcp_use_dns(context
, "", true);
770 r
= parse_boolean(value
);
774 return network_set_dhcp_use_dns(context
, "", r
);
777 static int parse_cmdline_vlan(Context
*context
, const char *key
, const char *value
) {
778 const char *name
, *p
;
782 if (proc_cmdline_value_missing(key
, value
))
785 p
= strchr(value
, ':');
789 name
= strndupa(value
, p
- value
);
791 netdev
= netdev_get(context
, name
);
793 r
= netdev_new(context
, "vlan", name
, &netdev
);
798 return network_set_vlan(context
, p
+ 1, name
);
801 static int parse_cmdline_bridge(Context
*context
, const char *key
, const char *value
) {
802 const char *name
, *p
;
806 if (proc_cmdline_value_missing(key
, value
))
809 p
= strchr(value
, ':');
813 name
= strndupa(value
, p
- value
);
815 netdev
= netdev_get(context
, name
);
817 r
= netdev_new(context
, "bridge", name
, &netdev
);
827 _cleanup_free_
char *word
= NULL
;
829 r
= extract_first_word(&p
, &word
, ",", 0);
833 r
= network_set_bridge(context
, word
, name
);
839 static int parse_cmdline_bond(Context
*context
, const char *key
, const char *value
) {
840 const char *name
, *slaves
, *p
;
844 if (proc_cmdline_value_missing(key
, value
))
847 p
= strchr(value
, ':');
851 name
= strndupa(value
, p
- value
);
853 netdev
= netdev_get(context
, name
);
855 r
= netdev_new(context
, "bond", name
, &netdev
);
861 p
= strchr(value
, ':');
865 slaves
= strndupa(value
, p
- value
);
870 for (const char *q
= slaves
; ; ) {
871 _cleanup_free_
char *word
= NULL
;
873 r
= extract_first_word(&q
, &word
, ",", 0);
879 r
= network_set_bond(context
, word
, name
);
888 p
= strchr(value
, ':');
890 /* TODO: set bonding options */
893 return parse_mtu(AF_UNSPEC
, p
+ 1, &netdev
->mtu
);
896 static int parse_cmdline_ifname(Context
*context
, const char *key
, const char *value
) {
897 struct ether_addr mac
;
898 const char *name
, *p
;
901 /* ifname=<interface>:<MAC> */
903 if (proc_cmdline_value_missing(key
, value
))
906 p
= strchr(value
, ':');
910 name
= strndupa(value
, p
- value
);
912 r
= ether_addr_from_string(p
+ 1, &mac
);
916 return link_new(context
, name
, &mac
, NULL
);
919 int parse_cmdline_item(const char *key
, const char *value
, void *data
) {
920 Context
*context
= data
;
925 if (streq(key
, "ip"))
926 return parse_cmdline_ip(context
, key
, value
);
927 if (streq(key
, "rd.route"))
928 return parse_cmdline_rd_route(context
, key
, value
);
929 if (streq(key
, "nameserver"))
930 return parse_cmdline_nameserver(context
, key
, value
);
931 if (streq(key
, "rd.peerdns"))
932 return parse_cmdline_rd_peerdns(context
, key
, value
);
933 if (streq(key
, "vlan"))
934 return parse_cmdline_vlan(context
, key
, value
);
935 if (streq(key
, "bridge"))
936 return parse_cmdline_bridge(context
, key
, value
);
937 if (streq(key
, "bond"))
938 return parse_cmdline_bond(context
, key
, value
);
939 if (streq(key
, "ifname"))
940 return parse_cmdline_ifname(context
, key
, value
);
945 int context_merge_networks(Context
*context
) {
946 Network
*all
, *network
;
952 /* Copy settings about the following options
953 rd.route=<net>/<netmask>:<gateway>[:<interface>]
954 nameserver=<IP> [nameserver=<IP> ...]
957 all
= network_get(context
, "");
961 if (hashmap_size(context
->networks_by_name
) <= 1)
964 HASHMAP_FOREACH(network
, context
->networks_by_name
) {
968 network
->dhcp_use_dns
= all
->dhcp_use_dns
;
970 r
= strv_extend_strv(&network
->dns
, all
->dns
, false);
974 LIST_FOREACH(routes
, route
, all
->routes
) {
975 r
= route_new(network
, route
->family
, route
->prefixlen
, &route
->dest
, &route
->gateway
, NULL
);
981 assert_se(hashmap_remove(context
->networks_by_name
, "") == all
);
986 void context_clear(Context
*context
) {
990 hashmap_free_with_destructor(context
->networks_by_name
, network_free
);
991 hashmap_free_with_destructor(context
->netdevs_by_name
, netdev_free
);
992 hashmap_free_with_destructor(context
->links_by_name
, link_free
);
995 static int address_dump(Address
*address
, FILE *f
) {
996 _cleanup_free_
char *addr
= NULL
, *peer
= NULL
;
999 r
= in_addr_prefix_to_string(address
->family
, &address
->address
, address
->prefixlen
, &addr
);
1003 if (in_addr_is_null(address
->family
, &address
->peer
) == 0) {
1004 r
= in_addr_to_string(address
->family
, &address
->peer
, &peer
);
1015 fprintf(f
, "Peer=%s\n", peer
);
1020 static int route_dump(Route
*route
, FILE *f
) {
1021 _cleanup_free_
char *dest
= NULL
, *gateway
= NULL
;
1024 if (in_addr_is_null(route
->family
, &route
->dest
) == 0) {
1025 r
= in_addr_prefix_to_string(route
->family
, &route
->dest
, route
->prefixlen
, &dest
);
1030 r
= in_addr_to_string(route
->family
, &route
->gateway
, &gateway
);
1034 fputs("\n[Route]\n", f
);
1036 fprintf(f
, "Destination=%s\n", dest
);
1037 fprintf(f
, "Gateway=%s\n", gateway
);
1042 void network_dump(Network
*network
, FILE *f
) {
1043 char mac
[ETHER_ADDR_TO_STRING_MAX
];
1055 isempty(network
->ifname
) ? "*" : network
->ifname
);
1057 fputs("\n[Link]\n", f
);
1059 if (!ether_addr_is_null(&network
->mac
))
1060 fprintf(f
, "MACAddress=%s\n", ether_addr_to_string(&network
->mac
, mac
));
1061 if (network
->mtu
> 0)
1062 fprintf(f
, "MTUBytes=%" PRIu32
"\n", network
->mtu
);
1064 fputs("\n[Network]\n", f
);
1066 dhcp
= networkd_dhcp_type_to_string(network
->dhcp_type
);
1068 fprintf(f
, "DHCP=%s\n", dhcp
);
1070 if (!strv_isempty(network
->dns
))
1071 STRV_FOREACH(dns
, network
->dns
)
1072 fprintf(f
, "DNS=%s\n", *dns
);
1075 fprintf(f
, "VLAN=%s\n", network
->vlan
);
1077 if (network
->bridge
)
1078 fprintf(f
, "Bridge=%s\n", network
->bridge
);
1081 fprintf(f
, "Bond=%s\n", network
->bond
);
1083 fputs("\n[DHCP]\n", f
);
1085 if (!isempty(network
->hostname
))
1086 fprintf(f
, "Hostname=%s\n", network
->hostname
);
1088 if (network
->dhcp_use_dns
>= 0)
1089 fprintf(f
, "UseDNS=%s\n", yes_no(network
->dhcp_use_dns
));
1091 LIST_FOREACH(addresses
, address
, network
->addresses
)
1092 (void) address_dump(address
, f
);
1094 LIST_FOREACH(routes
, route
, network
->routes
)
1095 (void) route_dump(route
, f
);
1098 void netdev_dump(NetDev
*netdev
, FILE *f
) {
1109 if (netdev
->mtu
> 0)
1110 fprintf(f
, "MTUBytes=%" PRIu32
"\n", netdev
->mtu
);
1113 void link_dump(Link
*link
, FILE *f
) {
1114 char mac
[ETHER_ADDR_TO_STRING_MAX
];
1119 fputs("[Match]\n", f
);
1121 if (!ether_addr_is_null(&link
->mac
))
1122 fprintf(f
, "MACAddress=%s\n", ether_addr_to_string(&link
->mac
, mac
));
1130 int network_format(Network
*network
, char **ret
) {
1131 _cleanup_free_
char *s
= NULL
;
1139 _cleanup_fclose_
FILE *f
= NULL
;
1141 f
= open_memstream_unlocked(&s
, &sz
);
1145 network_dump(network
, f
);
1147 /* Add terminating 0, so that the output buffer is a valid string. */
1150 r
= fflush_and_check(f
);
1158 return (int) sz
- 1;
1161 int netdev_format(NetDev
*netdev
, char **ret
) {
1162 _cleanup_free_
char *s
= NULL
;
1170 _cleanup_fclose_
FILE *f
= NULL
;
1172 f
= open_memstream_unlocked(&s
, &sz
);
1176 netdev_dump(netdev
, f
);
1178 /* Add terminating 0, so that the output buffer is a valid string. */
1181 r
= fflush_and_check(f
);
1189 return (int) sz
- 1;
1192 int link_format(Link
*link
, char **ret
) {
1193 _cleanup_free_
char *s
= NULL
;
1201 _cleanup_fclose_
FILE *f
= NULL
;
1203 f
= open_memstream_unlocked(&s
, &sz
);
1209 /* Add terminating 0, so that the output buffer is a valid string. */
1212 r
= fflush_and_check(f
);
1220 return (int) sz
- 1;