]>
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_allocated(&context
->networks_by_name
, &string_hash_ops
);
197 r
= hashmap_put(context
->networks_by_name
, network
->ifname
, network
);
208 Network
*network_get(Context
*context
, const char *ifname
) {
209 return hashmap_get(context
->networks_by_name
, ifname
);
212 static NetDev
*netdev_free(NetDev
*netdev
) {
216 free(netdev
->ifname
);
218 return mfree(netdev
);
221 DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev
*, netdev_free
);
223 static int netdev_new(Context
*context
, const char *_kind
, const char *_ifname
, NetDev
**ret
) {
224 _cleanup_(netdev_freep
) NetDev
*netdev
= NULL
;
225 _cleanup_free_
char *kind
= NULL
, *ifname
= NULL
;
230 if (!ifname_valid(_ifname
))
233 kind
= strdup(_kind
);
237 ifname
= strdup(_ifname
);
241 netdev
= new(NetDev
, 1);
246 .kind
= TAKE_PTR(kind
),
247 .ifname
= TAKE_PTR(ifname
),
250 r
= hashmap_ensure_allocated(&context
->netdevs_by_name
, &string_hash_ops
);
254 r
= hashmap_put(context
->netdevs_by_name
, netdev
->ifname
, netdev
);
265 NetDev
*netdev_get(Context
*context
, const char *ifname
) {
266 return hashmap_get(context
->netdevs_by_name
, ifname
);
269 static Link
*link_free(Link
*link
) {
277 DEFINE_TRIVIAL_CLEANUP_FUNC(Link
*, link_free
);
279 static int link_new(Context
*context
, const char *name
, struct ether_addr
*mac
, Link
**ret
) {
280 _cleanup_(link_freep
) Link
*link
= NULL
;
281 _cleanup_free_
char *ifname
= NULL
;
286 if (!ifname_valid(name
))
289 ifname
= strdup(name
);
298 .ifname
= TAKE_PTR(ifname
),
302 r
= hashmap_ensure_allocated(&context
->links_by_name
, &string_hash_ops
);
306 r
= hashmap_put(context
->links_by_name
, link
->ifname
, link
);
317 Link
*link_get(Context
*context
, const char *ifname
) {
318 return hashmap_get(context
->links_by_name
, ifname
);
321 static int network_set_dhcp_type(Context
*context
, const char *ifname
, const char *dhcp_type
) {
326 t
= dracut_dhcp_type_from_string(dhcp_type
);
330 network
= network_get(context
, ifname
);
332 r
= network_new(context
, ifname
, &network
);
337 network
->dhcp_type
= t
;
341 static int network_set_hostname(Context
*context
, const char *ifname
, const char *hostname
) {
344 network
= network_get(context
, ifname
);
348 return free_and_strdup(&network
->hostname
, hostname
);
351 static int network_set_mtu(Context
*context
, const char *ifname
, int family
, const char *mtu
) {
354 network
= network_get(context
, ifname
);
358 return parse_mtu(family
, mtu
, &network
->mtu
);
361 static int network_set_mac_address(Context
*context
, const char *ifname
, const char *mac
) {
364 network
= network_get(context
, ifname
);
368 return ether_addr_from_string(mac
, &network
->mac
);
371 static int network_set_address(Context
*context
, const char *ifname
, int family
, unsigned char prefixlen
,
372 union in_addr_union
*addr
, union in_addr_union
*peer
) {
375 if (in_addr_is_null(family
, addr
) != 0)
378 network
= network_get(context
, ifname
);
382 return address_new(network
, family
, prefixlen
, addr
, peer
, NULL
);
385 static int network_set_route(Context
*context
, const char *ifname
, int family
, unsigned char prefixlen
,
386 union in_addr_union
*dest
, union in_addr_union
*gateway
) {
390 if (in_addr_is_null(family
, gateway
) != 0)
393 network
= network_get(context
, ifname
);
395 r
= network_new(context
, ifname
, &network
);
400 return route_new(network
, family
, prefixlen
, dest
, gateway
, NULL
);
403 static int network_set_dns(Context
*context
, const char *ifname
, const char *dns
) {
404 union in_addr_union a
;
408 r
= in_addr_from_string_auto(dns
, &family
, &a
);
412 network
= network_get(context
, ifname
);
414 r
= network_new(context
, ifname
, &network
);
419 return strv_extend(&network
->dns
, dns
);
422 static int network_set_dhcp_use_dns(Context
*context
, const char *ifname
, bool value
) {
426 network
= network_get(context
, ifname
);
428 r
= network_new(context
, ifname
, &network
);
433 network
->dhcp_use_dns
= value
;
438 static int network_set_vlan(Context
*context
, const char *ifname
, const char *value
) {
442 network
= network_get(context
, ifname
);
444 r
= network_new(context
, ifname
, &network
);
449 return free_and_strdup(&network
->vlan
, value
);
452 static int network_set_bridge(Context
*context
, const char *ifname
, const char *value
) {
456 network
= network_get(context
, ifname
);
458 r
= network_new(context
, ifname
, &network
);
463 return free_and_strdup(&network
->bridge
, value
);
466 static int network_set_bond(Context
*context
, const char *ifname
, const char *value
) {
470 network
= network_get(context
, ifname
);
472 r
= network_new(context
, ifname
, &network
);
477 return free_and_strdup(&network
->bond
, value
);
480 static int parse_cmdline_ip_mtu_mac(Context
*context
, const char *ifname
, int family
, const char *value
) {
484 /* [<mtu>][:<macaddr>] */
486 p
= strchr(value
, ':');
490 mtu
= strndupa(value
, p
- value
);
492 r
= network_set_mtu(context
, ifname
, family
, mtu
);
499 r
= network_set_mac_address(context
, ifname
, p
+ 1);
506 static int parse_ip_address_one(int family
, const char **value
, union in_addr_union
*ret
) {
507 const char *p
= *value
, *q
, *buf
;
515 if (family
== AF_INET6
) {
519 q
= strchr(p
+ 1, ']');
526 buf
= strndupa(p
+ 1, q
- p
- 1);
533 buf
= strndupa(p
, q
- p
);
537 r
= in_addr_from_string(family
, buf
, ret
);
545 static int parse_netmask_or_prefixlen(int family
, const char **value
, unsigned char *ret
) {
546 union in_addr_union netmask
;
550 r
= parse_ip_address_one(family
, value
, &netmask
);
552 if (family
== AF_INET6
)
553 /* TODO: Not supported yet. */
556 *ret
= in4_addr_netmask_to_prefixlen(&netmask
.in
);
558 *ret
= family
== AF_INET6
? 128 : 32;
560 p
= strchr(*value
, ':');
564 q
= strndupa(*value
, p
- *value
);
565 r
= safe_atou8(q
, ret
);
575 static int parse_cmdline_ip_address(Context
*context
, int family
, const char *value
) {
576 union in_addr_union addr
= {}, peer
= {}, gateway
= {};
577 const char *hostname
= NULL
, *ifname
, *dhcp_type
, *dns
, *p
;
578 unsigned char prefixlen
;
581 /* ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>]]
582 * ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]] */
584 r
= parse_ip_address_one(family
, &value
, &addr
);
587 r
= parse_ip_address_one(family
, &value
, &peer
);
590 r
= parse_ip_address_one(family
, &value
, &gateway
);
593 r
= parse_netmask_or_prefixlen(family
, &value
, &prefixlen
);
598 p
= strchr(value
, ':');
603 hostname
= strndupa(value
, p
- value
);
604 if (!hostname_is_valid(hostname
, 0))
611 p
= strchr(value
, ':');
615 ifname
= strndupa(value
, p
- value
);
620 p
= strchr(value
, ':');
624 dhcp_type
= strndupa(value
, p
- value
);
626 r
= network_set_dhcp_type(context
, ifname
, dhcp_type
);
631 r
= network_set_hostname(context
, ifname
, hostname
);
635 r
= network_set_address(context
, ifname
, family
, prefixlen
, &addr
, &peer
);
639 r
= network_set_route(context
, ifname
, family
, 0, NULL
, &gateway
);
646 /* First, try [<mtu>][:<macaddr>] */
647 r
= parse_cmdline_ip_mtu_mac(context
, ifname
, AF_UNSPEC
, p
+ 1);
651 /* Next, try [<dns1>][:<dns2>] */
653 p
= strchr(value
, ':');
655 r
= network_set_dns(context
, ifname
, value
);
659 dns
= strndupa(value
, p
- value
);
660 r
= network_set_dns(context
, ifname
, dns
);
663 r
= network_set_dns(context
, ifname
, p
+ 1);
671 static int parse_cmdline_ip_interface(Context
*context
, const char *value
) {
672 const char *ifname
, *dhcp_type
, *p
;
675 /* ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]] */
677 p
= strchr(value
, ':');
681 ifname
= strndupa(value
, p
- value
);
684 p
= strchr(value
, ':');
688 dhcp_type
= strndupa(value
, p
- value
);
690 r
= network_set_dhcp_type(context
, ifname
, dhcp_type
);
697 return parse_cmdline_ip_mtu_mac(context
, ifname
, AF_UNSPEC
, p
+ 1);
700 static int parse_cmdline_ip(Context
*context
, const char *key
, const char *value
) {
704 if (proc_cmdline_value_missing(key
, value
))
707 p
= strchr(value
, ':');
709 /* ip={dhcp|on|any|dhcp6|auto6|either6} */
710 return network_set_dhcp_type(context
, "", value
);
713 return parse_cmdline_ip_address(context
, AF_INET6
, value
);
715 r
= parse_cmdline_ip_address(context
, AF_INET
, value
);
717 return parse_cmdline_ip_interface(context
, value
);
722 static int parse_cmdline_rd_route(Context
*context
, const char *key
, const char *value
) {
723 union in_addr_union addr
= {}, gateway
= {};
724 unsigned char prefixlen
;
728 /* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
730 if (proc_cmdline_value_missing(key
, value
))
733 if (value
[0] == '[') {
734 p
= strchr(value
, ']');
741 buf
= strndupa(value
+ 1, p
- value
- 1);
745 p
= strchr(value
, ':');
749 buf
= strndupa(value
, p
- value
);
754 r
= in_addr_prefix_from_string(buf
, family
, &addr
, &prefixlen
);
758 p
= strchr(value
, ':');
760 value
= strjoina(value
, ":");
762 r
= parse_ip_address_one(family
, &value
, &gateway
);
766 return network_set_route(context
, value
, family
, prefixlen
, &addr
, &gateway
);
769 static int parse_cmdline_nameserver(Context
*context
, const char *key
, const char *value
) {
770 if (proc_cmdline_value_missing(key
, value
))
773 return network_set_dns(context
, "", value
);
776 static int parse_cmdline_rd_peerdns(Context
*context
, const char *key
, const char *value
) {
779 if (proc_cmdline_value_missing(key
, value
))
780 return network_set_dhcp_use_dns(context
, "", true);
782 r
= parse_boolean(value
);
786 return network_set_dhcp_use_dns(context
, "", r
);
789 static int parse_cmdline_vlan(Context
*context
, const char *key
, const char *value
) {
790 const char *name
, *p
;
794 if (proc_cmdline_value_missing(key
, value
))
797 p
= strchr(value
, ':');
801 name
= strndupa(value
, p
- value
);
803 netdev
= netdev_get(context
, name
);
805 r
= netdev_new(context
, "vlan", name
, &netdev
);
810 return network_set_vlan(context
, p
+ 1, name
);
813 static int parse_cmdline_bridge(Context
*context
, const char *key
, const char *value
) {
814 const char *name
, *p
;
818 if (proc_cmdline_value_missing(key
, value
))
821 p
= strchr(value
, ':');
825 name
= strndupa(value
, p
- value
);
827 netdev
= netdev_get(context
, name
);
829 r
= netdev_new(context
, "bridge", name
, &netdev
);
839 _cleanup_free_
char *word
= NULL
;
841 r
= extract_first_word(&p
, &word
, ",", 0);
845 r
= network_set_bridge(context
, word
, name
);
851 static int parse_cmdline_bond(Context
*context
, const char *key
, const char *value
) {
852 const char *name
, *slaves
, *p
;
856 if (proc_cmdline_value_missing(key
, value
))
859 p
= strchr(value
, ':');
863 name
= strndupa(value
, p
- value
);
865 netdev
= netdev_get(context
, name
);
867 r
= netdev_new(context
, "bond", name
, &netdev
);
873 p
= strchr(value
, ':');
877 slaves
= strndupa(value
, p
- value
);
882 for (const char *q
= slaves
; ; ) {
883 _cleanup_free_
char *word
= NULL
;
885 r
= extract_first_word(&q
, &word
, ",", 0);
891 r
= network_set_bond(context
, word
, name
);
900 p
= strchr(value
, ':');
902 /* TODO: set bonding options */
905 return parse_mtu(AF_UNSPEC
, p
+ 1, &netdev
->mtu
);
908 static int parse_cmdline_ifname(Context
*context
, const char *key
, const char *value
) {
909 struct ether_addr mac
;
910 const char *name
, *p
;
913 /* ifname=<interface>:<MAC> */
915 if (proc_cmdline_value_missing(key
, value
))
918 p
= strchr(value
, ':');
922 name
= strndupa(value
, p
- value
);
924 r
= ether_addr_from_string(p
+ 1, &mac
);
928 return link_new(context
, name
, &mac
, NULL
);
931 int parse_cmdline_item(const char *key
, const char *value
, void *data
) {
932 Context
*context
= data
;
937 if (streq(key
, "ip"))
938 return parse_cmdline_ip(context
, key
, value
);
939 if (streq(key
, "rd.route"))
940 return parse_cmdline_rd_route(context
, key
, value
);
941 if (streq(key
, "nameserver"))
942 return parse_cmdline_nameserver(context
, key
, value
);
943 if (streq(key
, "rd.peerdns"))
944 return parse_cmdline_rd_peerdns(context
, key
, value
);
945 if (streq(key
, "vlan"))
946 return parse_cmdline_vlan(context
, key
, value
);
947 if (streq(key
, "bridge"))
948 return parse_cmdline_bridge(context
, key
, value
);
949 if (streq(key
, "bond"))
950 return parse_cmdline_bond(context
, key
, value
);
951 if (streq(key
, "ifname"))
952 return parse_cmdline_ifname(context
, key
, value
);
957 int context_merge_networks(Context
*context
) {
958 Network
*all
, *network
;
964 /* Copy settings about the following options
965 rd.route=<net>/<netmask>:<gateway>[:<interface>]
966 nameserver=<IP> [nameserver=<IP> ...]
969 all
= network_get(context
, "");
973 if (hashmap_size(context
->networks_by_name
) <= 1)
976 HASHMAP_FOREACH(network
, context
->networks_by_name
) {
980 network
->dhcp_use_dns
= all
->dhcp_use_dns
;
982 r
= strv_extend_strv(&network
->dns
, all
->dns
, false);
986 LIST_FOREACH(routes
, route
, all
->routes
) {
987 r
= route_new(network
, route
->family
, route
->prefixlen
, &route
->dest
, &route
->gateway
, NULL
);
993 assert_se(hashmap_remove(context
->networks_by_name
, "") == all
);
998 void context_clear(Context
*context
) {
1002 hashmap_free_with_destructor(context
->networks_by_name
, network_free
);
1003 hashmap_free_with_destructor(context
->netdevs_by_name
, netdev_free
);
1004 hashmap_free_with_destructor(context
->links_by_name
, link_free
);
1007 static int address_dump(Address
*address
, FILE *f
) {
1008 _cleanup_free_
char *addr
= NULL
, *peer
= NULL
;
1011 r
= in_addr_prefix_to_string(address
->family
, &address
->address
, address
->prefixlen
, &addr
);
1015 if (in_addr_is_null(address
->family
, &address
->peer
) == 0) {
1016 r
= in_addr_to_string(address
->family
, &address
->peer
, &peer
);
1027 fprintf(f
, "Peer=%s\n", peer
);
1032 static int route_dump(Route
*route
, FILE *f
) {
1033 _cleanup_free_
char *dest
= NULL
, *gateway
= NULL
;
1036 if (in_addr_is_null(route
->family
, &route
->dest
) == 0) {
1037 r
= in_addr_prefix_to_string(route
->family
, &route
->dest
, route
->prefixlen
, &dest
);
1042 r
= in_addr_to_string(route
->family
, &route
->gateway
, &gateway
);
1046 fputs("\n[Route]\n", f
);
1048 fprintf(f
, "Destination=%s\n", dest
);
1049 fprintf(f
, "Gateway=%s\n", gateway
);
1054 void network_dump(Network
*network
, FILE *f
) {
1055 char mac
[ETHER_ADDR_TO_STRING_MAX
];
1067 isempty(network
->ifname
) ? "*" : network
->ifname
);
1069 fputs("\n[Link]\n", f
);
1071 if (!ether_addr_is_null(&network
->mac
))
1072 fprintf(f
, "MACAddress=%s\n", ether_addr_to_string(&network
->mac
, mac
));
1073 if (network
->mtu
> 0)
1074 fprintf(f
, "MTUBytes=%" PRIu32
"\n", network
->mtu
);
1076 fputs("\n[Network]\n", f
);
1078 dhcp
= networkd_dhcp_type_to_string(network
->dhcp_type
);
1080 fprintf(f
, "DHCP=%s\n", dhcp
);
1082 if (!strv_isempty(network
->dns
))
1083 STRV_FOREACH(dns
, network
->dns
)
1084 fprintf(f
, "DNS=%s\n", *dns
);
1087 fprintf(f
, "VLAN=%s\n", network
->vlan
);
1089 if (network
->bridge
)
1090 fprintf(f
, "Bridge=%s\n", network
->bridge
);
1093 fprintf(f
, "Bond=%s\n", network
->bond
);
1095 fputs("\n[DHCP]\n", f
);
1097 if (!isempty(network
->hostname
))
1098 fprintf(f
, "Hostname=%s\n", network
->hostname
);
1100 if (network
->dhcp_use_dns
>= 0)
1101 fprintf(f
, "UseDNS=%s\n", yes_no(network
->dhcp_use_dns
));
1103 LIST_FOREACH(addresses
, address
, network
->addresses
)
1104 (void) address_dump(address
, f
);
1106 LIST_FOREACH(routes
, route
, network
->routes
)
1107 (void) route_dump(route
, f
);
1110 void netdev_dump(NetDev
*netdev
, FILE *f
) {
1121 if (netdev
->mtu
> 0)
1122 fprintf(f
, "MTUBytes=%" PRIu32
"\n", netdev
->mtu
);
1125 void link_dump(Link
*link
, FILE *f
) {
1126 char mac
[ETHER_ADDR_TO_STRING_MAX
];
1131 fputs("[Match]\n", f
);
1133 if (!ether_addr_is_null(&link
->mac
))
1134 fprintf(f
, "MACAddress=%s\n", ether_addr_to_string(&link
->mac
, mac
));
1142 int network_format(Network
*network
, char **ret
) {
1143 _cleanup_free_
char *s
= NULL
;
1151 _cleanup_fclose_
FILE *f
= NULL
;
1153 f
= open_memstream_unlocked(&s
, &sz
);
1157 network_dump(network
, f
);
1159 /* Add terminating 0, so that the output buffer is a valid string. */
1162 r
= fflush_and_check(f
);
1170 return (int) sz
- 1;
1173 int netdev_format(NetDev
*netdev
, char **ret
) {
1174 _cleanup_free_
char *s
= NULL
;
1182 _cleanup_fclose_
FILE *f
= NULL
;
1184 f
= open_memstream_unlocked(&s
, &sz
);
1188 netdev_dump(netdev
, f
);
1190 /* Add terminating 0, so that the output buffer is a valid string. */
1193 r
= fflush_and_check(f
);
1201 return (int) sz
- 1;
1204 int link_format(Link
*link
, char **ret
) {
1205 _cleanup_free_
char *s
= NULL
;
1213 _cleanup_fclose_
FILE *f
= NULL
;
1215 f
= open_memstream_unlocked(&s
, &sz
);
1221 /* Add terminating 0, so that the output buffer is a valid string. */
1224 r
= fflush_and_check(f
);
1232 return (int) sz
- 1;