Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include "ether-addr-util.h"
4 : : #include "fd-util.h"
5 : : #include "fileio.h"
6 : : #include "hostname-util.h"
7 : : #include "log.h"
8 : : #include "macro.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"
15 : : #include "strv.h"
16 : :
17 : : /*
18 : : # .network
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> ...]
25 : : rd.peerdns=0
26 : :
27 : : # .link
28 : : ifname=<interface>:<MAC>
29 : :
30 : : # .netdev
31 : : vlan=<vlanname>:<phydevice>
32 : : bond=<bondname>[:<bondslaves>:[:<options>[:<mtu>]]]
33 : : team=<teammaster>:<teamslaves> # not supported
34 : : bridge=<bridgename>:<ethnames>
35 : :
36 : : # ignored
37 : : bootdev=<interface>
38 : : BOOTIF=<MAC>
39 : : rd.bootif=0
40 : : biosdevname=0
41 : : rd.neednet=1
42 : : */
43 : :
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",
54 : : };
55 : :
56 : 68 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dracut_dhcp_type, DHCPType);
57 : :
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",
68 : : };
69 : :
70 [ + + - + ]: 108 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(networkd_dhcp_type, DHCPType);
71 : :
72 : 52 : static Address *address_free(Address *address) {
73 [ - + ]: 52 : if (!address)
74 : 0 : return NULL;
75 : :
76 [ + - ]: 52 : if (address->network)
77 [ - + - + : 52 : LIST_REMOVE(addresses, address->network->addresses, address);
- + - + ]
78 : :
79 : 52 : return mfree(address);
80 : : }
81 : :
82 : 52 : static int address_new(Network *network, int family, unsigned char prefixlen,
83 : : union in_addr_union *addr, union in_addr_union *peer, Address **ret) {
84 : : Address *address;
85 : :
86 [ - + ]: 52 : assert(network);
87 : :
88 : 52 : address = new(Address, 1);
89 [ - + ]: 52 : if (!address)
90 : 0 : return -ENOMEM;
91 : :
92 : 52 : *address = (Address) {
93 : : .family = family,
94 : : .prefixlen = prefixlen,
95 : 52 : .address = *addr,
96 : 52 : .peer = *peer,
97 : : };
98 : :
99 [ - + - + ]: 52 : LIST_PREPEND(addresses, network->addresses, address);
100 : :
101 : 52 : address->network = network;
102 : :
103 [ - + ]: 52 : if (ret)
104 : 0 : *ret = address;
105 : 52 : return 0;
106 : : }
107 : :
108 : 68 : static Route *route_free(Route *route) {
109 [ - + ]: 68 : if (!route)
110 : 0 : return NULL;
111 : :
112 [ + - ]: 68 : if (route->network)
113 [ - + + + : 68 : LIST_REMOVE(routes, route->network->routes, route);
- + - + ]
114 : :
115 : 68 : return mfree(route);
116 : : }
117 : :
118 : 68 : static int route_new(Network *network, int family, unsigned char prefixlen,
119 : : union in_addr_union *dest, union in_addr_union *gateway, Route **ret) {
120 : : Route *route;
121 : :
122 [ - + ]: 68 : assert(network);
123 : :
124 : 68 : route = new(Route, 1);
125 [ - + ]: 68 : if (!route)
126 : 0 : return -ENOMEM;
127 : :
128 : 68 : *route = (Route) {
129 : : .family = family,
130 : : .prefixlen = prefixlen,
131 [ + + ]: 68 : .dest = dest ? *dest : IN_ADDR_NULL,
132 : 68 : .gateway = *gateway,
133 : : };
134 : :
135 [ - + + + ]: 68 : LIST_PREPEND(routes, network->routes, route);
136 : :
137 : 68 : route->network = network;
138 : :
139 [ - + ]: 68 : if (ret)
140 : 0 : *ret = route;
141 : 68 : return 0;
142 : : }
143 : :
144 : 152 : static Network *network_free(Network *network) {
145 : : Address *address;
146 : : Route *route;
147 : :
148 [ - + ]: 152 : if (!network)
149 : 0 : return NULL;
150 : :
151 : 152 : free(network->ifname);
152 : 152 : free(network->hostname);
153 : 152 : strv_free(network->dns);
154 : 152 : free(network->vlan);
155 : 152 : free(network->bridge);
156 : 152 : free(network->bond);
157 : :
158 [ + + ]: 204 : while ((address = network->addresses))
159 : 52 : address_free(address);
160 : :
161 [ + + ]: 220 : while ((route = network->routes))
162 : 68 : route_free(route);
163 : :
164 : 152 : return mfree(network);
165 : : }
166 : :
167 [ - + ]: 152 : DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
168 : :
169 : 152 : static int network_new(Context *context, const char *name, Network **ret) {
170 : 152 : _cleanup_(network_freep) Network *network = NULL;
171 : 152 : _cleanup_free_ char *ifname = NULL;
172 : : int r;
173 : :
174 [ - + ]: 152 : assert(context);
175 : :
176 [ + + - + ]: 152 : if (!isempty(name) && !ifname_valid(name))
177 : 0 : return -EINVAL;
178 : :
179 : 152 : ifname = strdup(name);
180 [ - + ]: 152 : if (!ifname)
181 : 0 : return -ENOMEM;
182 : :
183 : 152 : network = new(Network, 1);
184 [ - + ]: 152 : if (!network)
185 : 0 : return -ENOMEM;
186 : :
187 : 304 : *network = (Network) {
188 : 152 : .ifname = TAKE_PTR(ifname),
189 : : .dhcp_type = _DHCP_TYPE_INVALID,
190 : : .dhcp_use_dns = -1,
191 : : };
192 : :
193 : 152 : r = hashmap_ensure_allocated(&context->networks_by_name, &string_hash_ops);
194 [ - + ]: 152 : if (r < 0)
195 : 0 : return r;
196 : :
197 : 152 : r = hashmap_put(context->networks_by_name, network->ifname, network);
198 [ - + ]: 152 : if (r < 0)
199 : 0 : return r;
200 : :
201 [ + - ]: 152 : if (ret)
202 : 152 : *ret = network;
203 : :
204 : 152 : TAKE_PTR(network);
205 : 152 : return 0;
206 : : }
207 : :
208 : 532 : Network *network_get(Context *context, const char *ifname) {
209 : 532 : return hashmap_get(context->networks_by_name, ifname);
210 : : }
211 : :
212 : 28 : static NetDev *netdev_free(NetDev *netdev) {
213 [ - + ]: 28 : if (!netdev)
214 : 0 : return NULL;
215 : :
216 : 28 : free(netdev->ifname);
217 : 28 : free(netdev->kind);
218 : 28 : return mfree(netdev);
219 : : }
220 : :
221 [ - + ]: 28 : DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_free);
222 : :
223 : 28 : static int netdev_new(Context *context, const char *_kind, const char *_ifname, NetDev **ret) {
224 : 28 : _cleanup_(netdev_freep) NetDev *netdev = NULL;
225 : 28 : _cleanup_free_ char *kind = NULL, *ifname = NULL;
226 : : int r;
227 : :
228 [ - + ]: 28 : assert(context);
229 : :
230 [ - + ]: 28 : if (!ifname_valid(_ifname))
231 : 0 : return -EINVAL;
232 : :
233 : 28 : kind = strdup(_kind);
234 [ - + ]: 28 : if (!kind)
235 : 0 : return -ENOMEM;
236 : :
237 : 28 : ifname = strdup(_ifname);
238 [ - + ]: 28 : if (!ifname)
239 : 0 : return -ENOMEM;
240 : :
241 : 28 : netdev = new(NetDev, 1);
242 [ - + ]: 28 : if (!netdev)
243 : 0 : return -ENOMEM;
244 : :
245 : 56 : *netdev = (NetDev) {
246 : 28 : .kind = TAKE_PTR(kind),
247 : 28 : .ifname = TAKE_PTR(ifname),
248 : : };
249 : :
250 : 28 : r = hashmap_ensure_allocated(&context->netdevs_by_name, &string_hash_ops);
251 [ - + ]: 28 : if (r < 0)
252 : 0 : return r;
253 : :
254 : 28 : r = hashmap_put(context->netdevs_by_name, netdev->ifname, netdev);
255 [ - + ]: 28 : if (r < 0)
256 : 0 : return r;
257 : :
258 [ + - ]: 28 : if (ret)
259 : 28 : *ret = netdev;
260 : :
261 : 28 : TAKE_PTR(netdev);
262 : 28 : return 0;
263 : : }
264 : :
265 : 32 : NetDev *netdev_get(Context *context, const char *ifname) {
266 : 32 : return hashmap_get(context->netdevs_by_name, ifname);
267 : : }
268 : :
269 : 4 : static Link *link_free(Link *link) {
270 [ - + ]: 4 : if (!link)
271 : 0 : return NULL;
272 : :
273 : 4 : free(link->ifname);
274 : 4 : return mfree(link);
275 : : }
276 : :
277 [ - + ]: 4 : DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
278 : :
279 : 4 : static int link_new(Context *context, const char *name, struct ether_addr *mac, Link **ret) {
280 : 4 : _cleanup_(link_freep) Link *link = NULL;
281 : 4 : _cleanup_free_ char *ifname = NULL;
282 : : int r;
283 : :
284 [ - + ]: 4 : assert(context);
285 : :
286 [ - + ]: 4 : if (!ifname_valid(name))
287 : 0 : return -EINVAL;
288 : :
289 : 4 : ifname = strdup(name);
290 [ - + ]: 4 : if (!ifname)
291 : 0 : return -ENOMEM;
292 : :
293 : 4 : link = new(Link, 1);
294 [ - + ]: 4 : if (!link)
295 : 0 : return -ENOMEM;
296 : :
297 : 8 : *link = (Link) {
298 : 4 : .ifname = TAKE_PTR(ifname),
299 : 4 : .mac = *mac,
300 : : };
301 : :
302 : 4 : r = hashmap_ensure_allocated(&context->links_by_name, &string_hash_ops);
303 [ - + ]: 4 : if (r < 0)
304 : 0 : return r;
305 : :
306 : 4 : r = hashmap_put(context->links_by_name, link->ifname, link);
307 [ - + ]: 4 : if (r < 0)
308 : 0 : return r;
309 : :
310 [ - + ]: 4 : if (ret)
311 : 0 : *ret = link;
312 : :
313 : 4 : TAKE_PTR(link);
314 : 4 : return 0;
315 : : }
316 : :
317 : 4 : Link *link_get(Context *context, const char *ifname) {
318 : 4 : return hashmap_get(context->links_by_name, ifname);
319 : : }
320 : :
321 : 68 : static int network_set_dhcp_type(Context *context, const char *ifname, const char *dhcp_type) {
322 : : Network *network;
323 : : DHCPType t;
324 : : int r;
325 : :
326 : 68 : t = dracut_dhcp_type_from_string(dhcp_type);
327 [ - + ]: 68 : if (t < 0)
328 : 0 : return -EINVAL;
329 : :
330 : 68 : network = network_get(context, ifname);
331 [ + - ]: 68 : if (!network) {
332 : 68 : r = network_new(context, ifname, &network);
333 [ - + ]: 68 : if (r < 0)
334 : 0 : return r;
335 : : }
336 : :
337 : 68 : network->dhcp_type = t;
338 : 68 : return 0;
339 : : }
340 : :
341 : 52 : static int network_set_hostname(Context *context, const char *ifname, const char *hostname) {
342 : : Network *network;
343 : :
344 : 52 : network = network_get(context, ifname);
345 [ - + ]: 52 : if (!network)
346 : 0 : return -ENODEV;
347 : :
348 : 52 : return free_and_strdup(&network->hostname, hostname);
349 : : }
350 : :
351 : 40 : static int network_set_mtu(Context *context, const char *ifname, int family, const char *mtu) {
352 : : Network *network;
353 : :
354 : 40 : network = network_get(context, ifname);
355 [ - + ]: 40 : if (!network)
356 : 0 : return -ENODEV;
357 : :
358 : 40 : return parse_mtu(family, mtu, &network->mtu);
359 : : }
360 : :
361 : 8 : static int network_set_mac_address(Context *context, const char *ifname, const char *mac) {
362 : : Network *network;
363 : :
364 : 8 : network = network_get(context, ifname);
365 [ - + ]: 8 : if (!network)
366 : 0 : return -ENODEV;
367 : :
368 : 8 : return ether_addr_from_string(mac, &network->mac);
369 : : }
370 : :
371 : 52 : 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) {
373 : : Network *network;
374 : :
375 [ - + ]: 52 : if (in_addr_is_null(family, addr) != 0)
376 : 0 : return 0;
377 : :
378 : 52 : network = network_get(context, ifname);
379 [ - + ]: 52 : if (!network)
380 : 0 : return -ENODEV;
381 : :
382 : 52 : return address_new(network, family, prefixlen, addr, peer, NULL);
383 : : }
384 : :
385 : 64 : 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) {
387 : : Network *network;
388 : : int r;
389 : :
390 [ - + ]: 64 : if (in_addr_is_null(family, gateway) != 0)
391 : 0 : return 0;
392 : :
393 : 64 : network = network_get(context, ifname);
394 [ + + ]: 64 : if (!network) {
395 : 12 : r = network_new(context, ifname, &network);
396 [ - + ]: 12 : if (r < 0)
397 : 0 : return r;
398 : : }
399 : :
400 : 64 : return route_new(network, family, prefixlen, dest, gateway, NULL);
401 : : }
402 : :
403 : 56 : static int network_set_dns(Context *context, const char *ifname, const char *dns) {
404 : : union in_addr_union a;
405 : : Network *network;
406 : : int family, r;
407 : :
408 : 56 : r = in_addr_from_string_auto(dns, &family, &a);
409 [ - + ]: 56 : if (r < 0)
410 : 0 : return r;
411 : :
412 : 56 : network = network_get(context, ifname);
413 [ + + ]: 56 : if (!network) {
414 : 12 : r = network_new(context, ifname, &network);
415 [ - + ]: 12 : if (r < 0)
416 : 0 : return r;
417 : : }
418 : :
419 : 56 : return strv_extend(&network->dns, dns);
420 : : }
421 : :
422 : 12 : static int network_set_dhcp_use_dns(Context *context, const char *ifname, bool value) {
423 : : Network *network;
424 : : int r;
425 : :
426 : 12 : network = network_get(context, ifname);
427 [ + - ]: 12 : if (!network) {
428 : 12 : r = network_new(context, ifname, &network);
429 [ - + ]: 12 : if (r < 0)
430 : 0 : return r;
431 : : }
432 : :
433 : 12 : network->dhcp_use_dns = value;
434 : :
435 : 12 : return 0;
436 : : }
437 : :
438 : 4 : static int network_set_vlan(Context *context, const char *ifname, const char *value) {
439 : : Network *network;
440 : : int r;
441 : :
442 : 4 : network = network_get(context, ifname);
443 [ + - ]: 4 : if (!network) {
444 : 4 : r = network_new(context, ifname, &network);
445 [ - + ]: 4 : if (r < 0)
446 : 0 : return r;
447 : : }
448 : :
449 : 4 : return free_and_strdup(&network->vlan, value);
450 : : }
451 : :
452 : 24 : static int network_set_bridge(Context *context, const char *ifname, const char *value) {
453 : : Network *network;
454 : : int r;
455 : :
456 : 24 : network = network_get(context, ifname);
457 [ + + ]: 24 : if (!network) {
458 : 20 : r = network_new(context, ifname, &network);
459 [ - + ]: 20 : if (r < 0)
460 : 0 : return r;
461 : : }
462 : :
463 : 24 : return free_and_strdup(&network->bridge, value);
464 : : }
465 : :
466 : 24 : static int network_set_bond(Context *context, const char *ifname, const char *value) {
467 : : Network *network;
468 : : int r;
469 : :
470 : 24 : network = network_get(context, ifname);
471 [ + - ]: 24 : if (!network) {
472 : 24 : r = network_new(context, ifname, &network);
473 [ - + ]: 24 : if (r < 0)
474 : 0 : return r;
475 : : }
476 : :
477 : 24 : return free_and_strdup(&network->bond, value);
478 : : }
479 : :
480 : 40 : static int parse_cmdline_ip_mtu_mac(Context *context, const char *ifname, int family, const char *value) {
481 : : const char *mtu, *p;
482 : : int r;
483 : :
484 : : /* [<mtu>][:<macaddr>] */
485 : :
486 : 40 : p = strchr(value, ':');
487 [ + + ]: 40 : if (!p)
488 : 12 : mtu = value;
489 : : else
490 : 28 : mtu = strndupa(value, p - value);
491 : :
492 : 40 : r = network_set_mtu(context, ifname, family, mtu);
493 [ + + ]: 40 : if (r < 0)
494 : 24 : return r;
495 : :
496 [ + + ]: 16 : if (!p)
497 : 8 : return 0;
498 : :
499 : 8 : r = network_set_mac_address(context, ifname, p + 1);
500 [ - + ]: 8 : if (r < 0)
501 : 0 : return r;
502 : :
503 : 8 : return 0;
504 : : }
505 : :
506 : 232 : static int parse_ip_address_one(int family, const char **value, union in_addr_union *ret) {
507 : 232 : const char *p = *value, *q, *buf;
508 : : int r;
509 : :
510 [ + + ]: 232 : if (p[0] == ':') {
511 : 8 : *value = p + 1;
512 : 8 : return 0;
513 : : }
514 : :
515 [ + + ]: 224 : if (family == AF_INET6) {
516 [ + + ]: 28 : if (p[0] != '[')
517 : 8 : return -EINVAL;
518 : :
519 : 20 : q = strchr(p + 1, ']');
520 [ - + ]: 20 : if (!q)
521 : 0 : return -EINVAL;
522 : :
523 [ - + ]: 20 : if (q[1] != ':')
524 : 0 : return -EINVAL;
525 : :
526 : 20 : buf = strndupa(p + 1, q - p - 1);
527 : 20 : p = q + 2;
528 : : } else {
529 : 196 : q = strchr(p, ':');
530 [ - + ]: 196 : if (!q)
531 : 0 : return -EINVAL;
532 : :
533 : 196 : buf = strndupa(p, q - p);
534 : 196 : p = q + 1;
535 : : }
536 : :
537 : 216 : r = in_addr_from_string(family, buf, ret);
538 [ + + ]: 216 : if (r < 0)
539 : 12 : return r;
540 : :
541 : 204 : *value = p;
542 : 204 : return 1;
543 : : }
544 : :
545 : 52 : static int parse_netmask_or_prefixlen(int family, const char **value, unsigned char *ret) {
546 : : union in_addr_union netmask;
547 : : const char *p, *q;
548 : : int r;
549 : :
550 : 52 : r = parse_ip_address_one(family, value, &netmask);
551 [ + + ]: 52 : if (r > 0) {
552 [ - + ]: 44 : if (family == AF_INET6)
553 : : /* TODO: Not supported yet. */
554 : 0 : return -EINVAL;
555 : :
556 : 44 : *ret = in4_addr_netmask_to_prefixlen(&netmask.in);
557 [ - + ]: 8 : } else if (r == 0)
558 [ # # ]: 0 : *ret = family == AF_INET6 ? 128 : 32;
559 : : else {
560 : 8 : p = strchr(*value, ':');
561 [ - + ]: 8 : if (!p)
562 : 0 : return -EINVAL;
563 : :
564 : 8 : q = strndupa(*value, p - *value);
565 : 8 : r = safe_atou8(q, ret);
566 [ - + ]: 8 : if (r < 0)
567 : 0 : return r;
568 : :
569 : 8 : *value = p + 1;
570 : : }
571 : :
572 : 52 : return 0;
573 : : }
574 : :
575 : 64 : static int parse_cmdline_ip_address(Context *context, int family, const char *value) {
576 : 64 : union in_addr_union addr = {}, peer = {}, gateway = {};
577 : : const char *hostname, *ifname, *dhcp_type, *dns, *p;
578 : : unsigned char prefixlen;
579 : : int r;
580 : :
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>]] */
583 : :
584 : 64 : r = parse_ip_address_one(family, &value, &addr);
585 [ + + ]: 64 : if (r < 0)
586 : 12 : return r;
587 : 52 : r = parse_ip_address_one(family, &value, &peer);
588 [ - + ]: 52 : if (r < 0)
589 : 0 : return r;
590 : 52 : r = parse_ip_address_one(family, &value, &gateway);
591 [ - + ]: 52 : if (r < 0)
592 : 0 : return r;
593 : 52 : r = parse_netmask_or_prefixlen(family, &value, &prefixlen);
594 [ - + ]: 52 : if (r < 0)
595 : 0 : return r;
596 : :
597 : : /* hostname */
598 : 52 : p = strchr(value, ':');
599 [ - + ]: 52 : if (!p)
600 : 0 : return -EINVAL;
601 : :
602 : 52 : hostname = strndupa(value, p - value);
603 [ - + ]: 52 : if (!hostname_is_valid(hostname, false))
604 : 0 : return -EINVAL;
605 : :
606 : 52 : value = p + 1;
607 : :
608 : : /* ifname */
609 : 52 : p = strchr(value, ':');
610 [ - + ]: 52 : if (!p)
611 : 0 : return -EINVAL;
612 : :
613 : 52 : ifname = strndupa(value, p - value);
614 : :
615 : 52 : value = p + 1;
616 : :
617 : : /* dhcp_type */
618 : 52 : p = strchr(value, ':');
619 [ + + ]: 52 : if (!p)
620 : 20 : dhcp_type = value;
621 : : else
622 : 32 : dhcp_type = strndupa(value, p - value);
623 : :
624 : 52 : r = network_set_dhcp_type(context, ifname, dhcp_type);
625 [ - + ]: 52 : if (r < 0)
626 : 0 : return r;
627 : :
628 : : /* set values */
629 : 52 : r = network_set_hostname(context, ifname, hostname);
630 [ - + ]: 52 : if (r < 0)
631 : 0 : return r;
632 : :
633 : 52 : r = network_set_address(context, ifname, family, prefixlen, &addr, &peer);
634 [ - + ]: 52 : if (r < 0)
635 : 0 : return r;
636 : :
637 : 52 : r = network_set_route(context, ifname, family, 0, NULL, &gateway);
638 [ - + ]: 52 : if (r < 0)
639 : 0 : return r;
640 : :
641 [ + + ]: 52 : if (!p)
642 : 20 : return 0;
643 : :
644 : : /* First, try [<mtu>][:<macaddr>] */
645 : 32 : r = parse_cmdline_ip_mtu_mac(context, ifname, AF_UNSPEC, p + 1);
646 [ + + ]: 32 : if (r >= 0)
647 : 8 : return 0;
648 : :
649 : : /* Next, try [<dns1>][:<dns2>] */
650 : 24 : value = p + 1;
651 : 24 : p = strchr(value, ':');
652 [ + + ]: 24 : if (!p) {
653 : 4 : r = network_set_dns(context, ifname, value);
654 [ - + ]: 4 : if (r < 0)
655 : 0 : return r;
656 : : } else {
657 : 20 : dns = strndupa(value, p - value);
658 : 20 : r = network_set_dns(context, ifname, dns);
659 [ - + ]: 20 : if (r < 0)
660 : 0 : return r;
661 : 20 : r = network_set_dns(context, ifname, p + 1);
662 [ - + ]: 20 : if (r < 0)
663 : 0 : return r;
664 : : }
665 : :
666 : 24 : return 0;
667 : : }
668 : :
669 : 12 : static int parse_cmdline_ip_interface(Context *context, const char *value) {
670 : : const char *ifname, *dhcp_type, *p;
671 : : int r;
672 : :
673 : : /* ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]] */
674 : :
675 : 12 : p = strchr(value, ':');
676 [ - + ]: 12 : if (!p)
677 : 0 : return -EINVAL;
678 : :
679 : 12 : ifname = strndupa(value, p - value);
680 : :
681 : 12 : value = p + 1;
682 : 12 : p = strchr(value, ':');
683 [ + + ]: 12 : if (!p)
684 : 4 : dhcp_type = value;
685 : : else
686 : 8 : dhcp_type = strndupa(value, p - value);
687 : :
688 : 12 : r = network_set_dhcp_type(context, ifname, dhcp_type);
689 [ - + ]: 12 : if (r < 0)
690 : 0 : return r;
691 : :
692 [ + + ]: 12 : if (!p)
693 : 4 : return 0;
694 : :
695 : 8 : return parse_cmdline_ip_mtu_mac(context, ifname, AF_UNSPEC, p + 1);
696 : : }
697 : :
698 : 68 : static int parse_cmdline_ip(Context *context, const char *key, const char *value) {
699 : : const char *p;
700 : : int r;
701 : :
702 [ - + ]: 68 : if (proc_cmdline_value_missing(key, value))
703 : 0 : return -EINVAL;
704 : :
705 : 68 : p = strchr(value, ':');
706 [ + + ]: 68 : if (!p)
707 : : /* ip={dhcp|on|any|dhcp6|auto6|either6} */
708 : 4 : return network_set_dhcp_type(context, "", value);
709 : :
710 [ + + ]: 64 : if (value[0] == '[')
711 : 8 : return parse_cmdline_ip_address(context, AF_INET6, value);
712 : :
713 : 56 : r = parse_cmdline_ip_address(context, AF_INET, value);
714 [ + + ]: 56 : if (r < 0)
715 : 12 : return parse_cmdline_ip_interface(context, value);
716 : :
717 : 44 : return 0;
718 : : }
719 : :
720 : 12 : static int parse_cmdline_rd_route(Context *context, const char *key, const char *value) {
721 : 12 : union in_addr_union addr = {}, gateway = {};
722 : : unsigned char prefixlen;
723 : : const char *buf, *p;
724 : : int family, r;
725 : :
726 : : /* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
727 : :
728 [ - + ]: 12 : if (proc_cmdline_value_missing(key, value))
729 : 0 : return -EINVAL;
730 : :
731 [ - + ]: 12 : if (value[0] == '[') {
732 : 0 : p = strchr(value, ']');
733 [ # # ]: 0 : if (!p)
734 : 0 : return -EINVAL;
735 : :
736 [ # # ]: 0 : if (p[1] != ':')
737 : 0 : return -EINVAL;
738 : :
739 : 0 : buf = strndupa(value + 1, p - value - 1);
740 : 0 : value = p + 2;
741 : 0 : family = AF_INET6;
742 : : } else {
743 : 12 : p = strchr(value, ':');
744 [ - + ]: 12 : if (!p)
745 : 0 : return -EINVAL;
746 : :
747 : 12 : buf = strndupa(value, p - value);
748 : 12 : value = p + 1;
749 : 12 : family = AF_INET;
750 : : }
751 : :
752 : 12 : r = in_addr_prefix_from_string(buf, family, &addr, &prefixlen);
753 [ - + ]: 12 : if (r < 0)
754 : 0 : return r;
755 : :
756 : 12 : p = strchr(value, ':');
757 [ + + ]: 12 : if (!p)
758 [ + + + - : 40 : value = strjoina(value, ":");
- + - + +
+ + - ]
759 : :
760 : 12 : r = parse_ip_address_one(family, &value, &gateway);
761 [ - + ]: 12 : if (r < 0)
762 : 0 : return r;
763 : :
764 : 12 : return network_set_route(context, value, family, prefixlen, &addr, &gateway);
765 : : }
766 : :
767 : 12 : static int parse_cmdline_nameserver(Context *context, const char *key, const char *value) {
768 [ - + ]: 12 : if (proc_cmdline_value_missing(key, value))
769 : 0 : return -EINVAL;
770 : :
771 : 12 : return network_set_dns(context, "", value);
772 : : }
773 : :
774 : 12 : static int parse_cmdline_rd_peerdns(Context *context, const char *key, const char *value) {
775 : : int r;
776 : :
777 [ - + ]: 12 : if (proc_cmdline_value_missing(key, value))
778 : 0 : return network_set_dhcp_use_dns(context, "", true);
779 : :
780 : 12 : r = parse_boolean(value);
781 [ - + ]: 12 : if (r < 0)
782 : 0 : return r;
783 : :
784 : 12 : return network_set_dhcp_use_dns(context, "", r);
785 : : }
786 : :
787 : 4 : static int parse_cmdline_vlan(Context *context, const char *key, const char *value) {
788 : : const char *name, *p;
789 : : NetDev *netdev;
790 : : int r;
791 : :
792 [ - + ]: 4 : if (proc_cmdline_value_missing(key, value))
793 : 0 : return -EINVAL;
794 : :
795 : 4 : p = strchr(value, ':');
796 [ - + ]: 4 : if (!p)
797 : 0 : return -EINVAL;
798 : :
799 : 4 : name = strndupa(value, p - value);
800 : :
801 : 4 : netdev = netdev_get(context, name);
802 [ + - ]: 4 : if (!netdev) {
803 : 4 : r = netdev_new(context, "vlan", name, &netdev);
804 [ - + ]: 4 : if (r < 0)
805 : 0 : return r;
806 : : }
807 : :
808 : 4 : return network_set_vlan(context, p + 1, name);
809 : : }
810 : :
811 : 12 : static int parse_cmdline_bridge(Context *context, const char *key, const char *value) {
812 : : const char *name, *p;
813 : : NetDev *netdev;
814 : : int r;
815 : :
816 [ - + ]: 12 : if (proc_cmdline_value_missing(key, value))
817 : 0 : return -EINVAL;
818 : :
819 : 12 : p = strchr(value, ':');
820 [ - + ]: 12 : if (!p)
821 : 0 : return -EINVAL;
822 : :
823 : 12 : name = strndupa(value, p - value);
824 : :
825 : 12 : netdev = netdev_get(context, name);
826 [ + - ]: 12 : if (!netdev) {
827 : 12 : r = netdev_new(context, "bridge", name, &netdev);
828 [ - + ]: 12 : if (r < 0)
829 : 0 : return r;
830 : : }
831 : :
832 : 12 : p++;
833 [ - + ]: 12 : if (isempty(p))
834 : 0 : return -EINVAL;
835 : :
836 : 24 : for (;;) {
837 [ + + ]: 36 : _cleanup_free_ char *word = NULL;
838 : :
839 : 36 : r = extract_first_word(&p, &word, ",", 0);
840 [ + + ]: 36 : if (r == 0)
841 : 12 : return 0;
842 [ - + ]: 24 : if (r < 0)
843 : 0 : return r;
844 : :
845 : 24 : r = network_set_bridge(context, word, name);
846 [ - + ]: 24 : if (r < 0)
847 : 0 : return r;
848 : : }
849 : : }
850 : :
851 : 12 : static int parse_cmdline_bond(Context *context, const char *key, const char *value) {
852 : : const char *name, *slaves, *p;
853 : : NetDev *netdev;
854 : : int r;
855 : :
856 [ - + ]: 12 : if (proc_cmdline_value_missing(key, value))
857 : 0 : return -EINVAL;
858 : :
859 : 12 : p = strchr(value, ':');
860 [ - + ]: 12 : if (!p)
861 : 0 : return -EINVAL;
862 : :
863 : 12 : name = strndupa(value, p - value);
864 : :
865 : 12 : netdev = netdev_get(context, name);
866 [ + - ]: 12 : if (!netdev) {
867 : 12 : r = netdev_new(context, "bond", name, &netdev);
868 [ - + ]: 12 : if (r < 0)
869 : 0 : return r;
870 : : }
871 : :
872 : 12 : value = p + 1;
873 : 12 : p = strchr(value, ':');
874 [ + + ]: 12 : if (!p)
875 : 4 : slaves = value;
876 : : else
877 : 8 : slaves = strndupa(value, p - value);
878 : :
879 [ - + ]: 12 : if (isempty(slaves))
880 : 0 : return -EINVAL;
881 : :
882 : 36 : for (const char *q = slaves; ; ) {
883 [ + + - ]: 36 : _cleanup_free_ char *word = NULL;
884 : :
885 : 36 : r = extract_first_word(&q, &word, ",", 0);
886 [ + + ]: 36 : if (r == 0)
887 : 12 : break;
888 [ - + ]: 24 : if (r < 0)
889 : 0 : return r;
890 : :
891 : 24 : r = network_set_bond(context, word, name);
892 [ - + ]: 24 : if (r < 0)
893 : 0 : return r;
894 : : }
895 : :
896 [ + + ]: 12 : if (!p)
897 : 4 : return 0;
898 : :
899 : 8 : value = p + 1;
900 : 8 : p = strchr(value, ':');
901 [ - + ]: 8 : if (!p)
902 : : /* TODO: set bonding options */
903 : 0 : return 0;
904 : :
905 : 8 : return parse_mtu(AF_UNSPEC, p + 1, &netdev->mtu);
906 : : }
907 : :
908 : 4 : static int parse_cmdline_ifname(Context *context, const char *key, const char *value) {
909 : : struct ether_addr mac;
910 : : const char *name, *p;
911 : : int r;
912 : :
913 : : /* ifname=<interface>:<MAC> */
914 : :
915 [ - + ]: 4 : if (proc_cmdline_value_missing(key, value))
916 : 0 : return -EINVAL;
917 : :
918 : 4 : p = strchr(value, ':');
919 [ - + ]: 4 : if (!p)
920 : 0 : return -EINVAL;
921 : :
922 : 4 : name = strndupa(value, p - value);
923 : :
924 : 4 : r = ether_addr_from_string(p + 1, &mac);
925 [ - + ]: 4 : if (r < 0)
926 : 0 : return r;
927 : :
928 : 4 : return link_new(context, name, &mac, NULL);
929 : : }
930 : :
931 : 136 : int parse_cmdline_item(const char *key, const char *value, void *data) {
932 : 136 : Context *context = data;
933 : :
934 [ - + ]: 136 : assert(key);
935 [ - + ]: 136 : assert(data);
936 : :
937 [ + + ]: 136 : if (streq(key, "ip"))
938 : 68 : return parse_cmdline_ip(context, key, value);
939 [ + + ]: 68 : if (streq(key, "rd.route"))
940 : 12 : return parse_cmdline_rd_route(context, key, value);
941 [ + + ]: 56 : if (streq(key, "nameserver"))
942 : 12 : return parse_cmdline_nameserver(context, key, value);
943 [ + + ]: 44 : if (streq(key, "rd.peerdns"))
944 : 12 : return parse_cmdline_rd_peerdns(context, key, value);
945 [ + + ]: 32 : if (streq(key, "vlan"))
946 : 4 : return parse_cmdline_vlan(context, key, value);
947 [ + + ]: 28 : if (streq(key, "bridge"))
948 : 12 : return parse_cmdline_bridge(context, key, value);
949 [ + + ]: 16 : if (streq(key, "bond"))
950 : 12 : return parse_cmdline_bond(context, key, value);
951 [ + - ]: 4 : if (streq(key, "ifname"))
952 : 4 : return parse_cmdline_ifname(context, key, value);
953 : :
954 : 0 : return 0;
955 : : }
956 : :
957 : 20 : int context_merge_networks(Context *context) {
958 : : Network *all, *network;
959 : : Route *route;
960 : : Iterator i;
961 : : int r;
962 : :
963 [ - + ]: 20 : assert(context);
964 : :
965 : : /* Copy settings about the following options
966 : : rd.route=<net>/<netmask>:<gateway>[:<interface>]
967 : : nameserver=<IP> [nameserver=<IP> ...]
968 : : rd.peerdns=0 */
969 : :
970 : 20 : all = network_get(context, "");
971 [ + + ]: 20 : if (!all)
972 : 4 : return 0;
973 : :
974 [ - + ]: 16 : if (hashmap_size(context->networks_by_name) <= 1)
975 : 0 : return 0;
976 : :
977 [ + + ]: 48 : HASHMAP_FOREACH(network, context->networks_by_name, i) {
978 [ + + ]: 32 : if (network == all)
979 : 16 : continue;
980 : :
981 : 16 : network->dhcp_use_dns = all->dhcp_use_dns;
982 : :
983 : 16 : r = strv_extend_strv(&network->dns, all->dns, false);
984 [ - + ]: 16 : if (r < 0)
985 : 0 : return r;
986 : :
987 [ + + ]: 20 : LIST_FOREACH(routes, route, all->routes) {
988 : 4 : r = route_new(network, route->family, route->prefixlen, &route->dest, &route->gateway, NULL);
989 [ - + ]: 4 : if (r < 0)
990 : 0 : return r;
991 : : }
992 : : }
993 : :
994 [ - + ]: 16 : assert_se(hashmap_remove(context->networks_by_name, "") == all);
995 : 16 : network_free(all);
996 : 16 : return 0;
997 : : }
998 : :
999 : 116 : void context_clear(Context *context) {
1000 [ - + ]: 116 : if (!context)
1001 : 0 : return;
1002 : :
1003 [ + + ]: 252 : hashmap_free_with_destructor(context->networks_by_name, network_free);
1004 [ + + ]: 144 : hashmap_free_with_destructor(context->netdevs_by_name, netdev_free);
1005 [ + + ]: 120 : hashmap_free_with_destructor(context->links_by_name, link_free);
1006 : : }
1007 : :
1008 : 52 : static int address_dump(Address *address, FILE *f) {
1009 : 52 : _cleanup_free_ char *addr = NULL, *peer = NULL;
1010 : : int r;
1011 : :
1012 : 52 : r = in_addr_prefix_to_string(address->family, &address->address, address->prefixlen, &addr);
1013 [ - + ]: 52 : if (r < 0)
1014 : 0 : return r;
1015 : :
1016 [ + + ]: 52 : if (in_addr_is_null(address->family, &address->peer) == 0) {
1017 : 44 : r = in_addr_to_string(address->family, &address->peer, &peer);
1018 [ - + ]: 44 : if (r < 0)
1019 : 0 : return r;
1020 : : }
1021 : :
1022 : 52 : fprintf(f,
1023 : : "\n[Address]\n"
1024 : : "Address=%s\n",
1025 : : addr);
1026 : :
1027 [ + + ]: 52 : if (peer)
1028 : 44 : fprintf(f, "Peer=%s\n", peer);
1029 : :
1030 : 52 : return 0;
1031 : : }
1032 : :
1033 : 64 : static int route_dump(Route *route, FILE *f) {
1034 : 64 : _cleanup_free_ char *dest = NULL, *gateway = NULL;
1035 : : int r;
1036 : :
1037 [ + + ]: 64 : if (in_addr_is_null(route->family, &route->dest) == 0) {
1038 : 12 : r = in_addr_prefix_to_string(route->family, &route->dest, route->prefixlen, &dest);
1039 [ - + ]: 12 : if (r < 0)
1040 : 0 : return r;
1041 : : }
1042 : :
1043 : 64 : r = in_addr_to_string(route->family, &route->gateway, &gateway);
1044 [ - + ]: 64 : if (r < 0)
1045 : 0 : return r;
1046 : :
1047 : 64 : fputs("\n[Route]\n", f);
1048 [ + + ]: 64 : if (dest)
1049 : 12 : fprintf(f, "Destination=%s\n", dest);
1050 : 64 : fprintf(f, "Gateway=%s\n", gateway);
1051 : :
1052 : 64 : return 0;
1053 : : }
1054 : :
1055 : 108 : void network_dump(Network *network, FILE *f) {
1056 : : char mac[ETHER_ADDR_TO_STRING_MAX];
1057 : : Address *address;
1058 : : Route *route;
1059 : : const char *dhcp;
1060 : : char **dns;
1061 : :
1062 [ - + ]: 108 : assert(network);
1063 [ - + ]: 108 : assert(f);
1064 : :
1065 [ + + ]: 108 : fprintf(f,
1066 : : "[Match]\n"
1067 : : "Name=%s\n",
1068 : 108 : isempty(network->ifname) ? "*" : network->ifname);
1069 : :
1070 : 108 : fputs("\n[Link]\n", f);
1071 : :
1072 [ + + ]: 108 : if (!ether_addr_is_null(&network->mac))
1073 : 8 : fprintf(f, "MACAddress=%s\n", ether_addr_to_string(&network->mac, mac));
1074 [ + + ]: 108 : if (network->mtu > 0)
1075 : 16 : fprintf(f, "MTUBytes=%" PRIu32 "\n", network->mtu);
1076 : :
1077 : 108 : fputs("\n[Network]\n", f);
1078 : :
1079 : 108 : dhcp = networkd_dhcp_type_to_string(network->dhcp_type);
1080 [ + + ]: 108 : if (dhcp)
1081 : 68 : fprintf(f, "DHCP=%s\n", dhcp);
1082 : :
1083 [ + + ]: 108 : if (!strv_isempty(network->dns))
1084 [ + - + + ]: 88 : STRV_FOREACH(dns, network->dns)
1085 : 56 : fprintf(f, "DNS=%s\n", *dns);
1086 : :
1087 [ + + ]: 108 : if (network->vlan)
1088 : 4 : fprintf(f, "VLAN=%s\n", network->vlan);
1089 : :
1090 [ + + ]: 108 : if (network->bridge)
1091 : 12 : fprintf(f, "Bridge=%s\n", network->bridge);
1092 : :
1093 [ + + ]: 108 : if (network->bond)
1094 : 8 : fprintf(f, "Bond=%s\n", network->bond);
1095 : :
1096 : 108 : fputs("\n[DHCP]\n", f);
1097 : :
1098 [ + + ]: 108 : if (!isempty(network->hostname))
1099 : 52 : fprintf(f, "Hostname=%s\n", network->hostname);
1100 : :
1101 [ + + ]: 108 : if (network->dhcp_use_dns >= 0)
1102 : 12 : fprintf(f, "UseDNS=%s\n", yes_no(network->dhcp_use_dns));
1103 : :
1104 [ + + ]: 160 : LIST_FOREACH(addresses, address, network->addresses)
1105 : 52 : (void) address_dump(address, f);
1106 : :
1107 [ + + ]: 172 : LIST_FOREACH(routes, route, network->routes)
1108 : 64 : (void) route_dump(route, f);
1109 : 108 : }
1110 : :
1111 : 4 : void netdev_dump(NetDev *netdev, FILE *f) {
1112 [ - + ]: 4 : assert(netdev);
1113 [ - + ]: 4 : assert(f);
1114 : :
1115 : 4 : fprintf(f,
1116 : : "[NetDev]\n"
1117 : : "Kind=%s\n"
1118 : : "Name=%s\n",
1119 : : netdev->kind,
1120 : : netdev->ifname);
1121 : :
1122 [ + - ]: 4 : if (netdev->mtu > 0)
1123 : 4 : fprintf(f, "MTUBytes=%" PRIu32 "\n", netdev->mtu);
1124 : 4 : }
1125 : :
1126 : 4 : void link_dump(Link *link, FILE *f) {
1127 : : char mac[ETHER_ADDR_TO_STRING_MAX];
1128 : :
1129 [ - + ]: 4 : assert(link);
1130 [ - + ]: 4 : assert(f);
1131 : :
1132 : 4 : fputs("[Match]\n", f);
1133 : :
1134 [ + - ]: 4 : if (!ether_addr_is_null(&link->mac))
1135 : 4 : fprintf(f, "MACAddress=%s\n", ether_addr_to_string(&link->mac, mac));
1136 : :
1137 : 4 : fprintf(f,
1138 : : "\n[Link]\n"
1139 : : "Name=%s\n",
1140 : : link->ifname);
1141 : 4 : }
1142 : :
1143 : 108 : int network_format(Network *network, char **ret) {
1144 : 108 : _cleanup_free_ char *s = NULL;
1145 : 108 : size_t sz = 0;
1146 : : int r;
1147 : :
1148 [ - + ]: 108 : assert(network);
1149 [ - + ]: 108 : assert(ret);
1150 : :
1151 : : {
1152 [ + - ]: 108 : _cleanup_fclose_ FILE *f = NULL;
1153 : :
1154 : 108 : f = open_memstream_unlocked(&s, &sz);
1155 [ - + ]: 108 : if (!f)
1156 : 0 : return -ENOMEM;
1157 : :
1158 : 108 : network_dump(network, f);
1159 : :
1160 : : /* Add terminating 0, so that the output buffer is a valid string. */
1161 : 108 : fputc('\0', f);
1162 : :
1163 : 108 : r = fflush_and_check(f);
1164 : : }
1165 [ - + ]: 108 : if (r < 0)
1166 : 0 : return r;
1167 : :
1168 [ - + ]: 108 : assert(s);
1169 : 108 : *ret = TAKE_PTR(s);
1170 [ - + ]: 108 : assert(sz > 0);
1171 : 108 : return (int) sz - 1;
1172 : : }
1173 : :
1174 : 4 : int netdev_format(NetDev *netdev, char **ret) {
1175 : 4 : _cleanup_free_ char *s = NULL;
1176 : 4 : size_t sz = 0;
1177 : : int r;
1178 : :
1179 [ - + ]: 4 : assert(netdev);
1180 [ - + ]: 4 : assert(ret);
1181 : :
1182 : : {
1183 [ + - ]: 4 : _cleanup_fclose_ FILE *f = NULL;
1184 : :
1185 : 4 : f = open_memstream_unlocked(&s, &sz);
1186 [ - + ]: 4 : if (!f)
1187 : 0 : return -ENOMEM;
1188 : :
1189 : 4 : netdev_dump(netdev, f);
1190 : :
1191 : : /* Add terminating 0, so that the output buffer is a valid string. */
1192 : 4 : fputc('\0', f);
1193 : :
1194 : 4 : r = fflush_and_check(f);
1195 : : }
1196 [ - + ]: 4 : if (r < 0)
1197 : 0 : return r;
1198 : :
1199 [ - + ]: 4 : assert(s);
1200 : 4 : *ret = TAKE_PTR(s);
1201 [ - + ]: 4 : assert(sz > 0);
1202 : 4 : return (int) sz - 1;
1203 : : }
1204 : :
1205 : 4 : int link_format(Link *link, char **ret) {
1206 : 4 : _cleanup_free_ char *s = NULL;
1207 : 4 : size_t sz = 0;
1208 : : int r;
1209 : :
1210 [ - + ]: 4 : assert(link);
1211 [ - + ]: 4 : assert(ret);
1212 : :
1213 : : {
1214 [ + - ]: 4 : _cleanup_fclose_ FILE *f = NULL;
1215 : :
1216 : 4 : f = open_memstream_unlocked(&s, &sz);
1217 [ - + ]: 4 : if (!f)
1218 : 0 : return -ENOMEM;
1219 : :
1220 : 4 : link_dump(link, f);
1221 : :
1222 : : /* Add terminating 0, so that the output buffer is a valid string. */
1223 : 4 : fputc('\0', f);
1224 : :
1225 : 4 : r = fflush_and_check(f);
1226 : : }
1227 [ - + ]: 4 : if (r < 0)
1228 : 0 : return r;
1229 : :
1230 [ - + ]: 4 : assert(s);
1231 : 4 : *ret = TAKE_PTR(s);
1232 [ - + ]: 4 : assert(sz > 0);
1233 : 4 : return (int) sz - 1;
1234 : : }
|