]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-link-bus.c
tree-wide: make parse_ifindex simply return the index
[thirdparty/systemd.git] / src / network / networkd-link-bus.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <net/if.h>
4 #include <netinet/in.h>
5 #include <sys/capability.h>
6
7 #include "alloc-util.h"
8 #include "bus-common-errors.h"
9 #include "bus-util.h"
10 #include "dns-domain.h"
11 #include "networkd-link-bus.h"
12 #include "networkd-link.h"
13 #include "networkd-manager.h"
14 #include "parse-util.h"
15 #include "resolve-util.h"
16 #include "strv.h"
17 #include "user-util.h"
18
19 BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
20 BUS_DEFINE_PROPERTY_GET_ENUM(property_get_carrier_state, link_carrier_state, LinkCarrierState);
21 BUS_DEFINE_PROPERTY_GET_ENUM(property_get_address_state, link_address_state, LinkAddressState);
22 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState);
23
24 static int property_get_bit_rates(
25 sd_bus *bus,
26 const char *path,
27 const char *interface,
28 const char *property,
29 sd_bus_message *reply,
30 void *userdata,
31 sd_bus_error *error) {
32
33 Link *link = userdata;
34 Manager *manager;
35 double interval_sec;
36 uint64_t tx, rx;
37
38 assert(bus);
39 assert(reply);
40 assert(userdata);
41
42 manager = link->manager;
43
44 if (!manager->use_speed_meter ||
45 manager->speed_meter_usec_old == 0 ||
46 !link->stats_updated)
47 return sd_bus_message_append(reply, "(tt)", UINT64_MAX, UINT64_MAX);
48
49 assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old);
50 interval_sec = (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC;
51
52 if (link->stats_new.tx_bytes > link->stats_old.tx_bytes)
53 tx = (uint64_t) ((link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec);
54 else
55 tx = (uint64_t) ((UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec);
56
57 if (link->stats_new.rx_bytes > link->stats_old.rx_bytes)
58 rx = (uint64_t) ((link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec);
59 else
60 rx = (uint64_t) ((UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec);
61
62 return sd_bus_message_append(reply, "(tt)", tx, rx);
63 }
64
65 static int verify_managed_link(Link *l, sd_bus_error *error) {
66 assert(l);
67
68 if (l->flags & IFF_LOOPBACK)
69 return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->ifname);
70
71 return 0;
72 }
73
74 int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
75 _cleanup_strv_free_ char **ntp = NULL;
76 Link *l = userdata;
77 int r;
78 char **i;
79
80 assert(message);
81 assert(l);
82
83 r = verify_managed_link(l, error);
84 if (r < 0)
85 return r;
86
87 r = sd_bus_message_read_strv(message, &ntp);
88 if (r < 0)
89 return r;
90
91 STRV_FOREACH(i, ntp) {
92 r = dns_name_is_valid_or_address(*i);
93 if (r < 0)
94 return r;
95 if (r == 0)
96 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i);
97 }
98
99 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
100 "org.freedesktop.network1.set-ntp-servers",
101 NULL, true, UID_INVALID,
102 &l->manager->polkit_registry, error);
103 if (r < 0)
104 return r;
105 if (r == 0)
106 return 1; /* Polkit will call us back */
107
108 strv_free_and_replace(l->ntp, ntp);
109
110 (void) link_dirty(l);
111
112 return sd_bus_reply_method_return(message, NULL);
113 }
114
115 int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
116 _cleanup_free_ struct in_addr_data *dns = NULL;
117 size_t allocated = 0, n = 0;
118 Link *l = userdata;
119 int r;
120
121 assert(message);
122 assert(l);
123
124 r = verify_managed_link(l, error);
125 if (r < 0)
126 return r;
127
128 r = sd_bus_message_enter_container(message, 'a', "(iay)");
129 if (r < 0)
130 return r;
131
132 for (;;) {
133 int family;
134 size_t sz;
135 const void *d;
136
137 assert_cc(sizeof(int) == sizeof(int32_t));
138
139 r = sd_bus_message_enter_container(message, 'r', "iay");
140 if (r < 0)
141 return r;
142 if (r == 0)
143 break;
144
145 r = sd_bus_message_read(message, "i", &family);
146 if (r < 0)
147 return r;
148
149 if (!IN_SET(family, AF_INET, AF_INET6))
150 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
151
152 r = sd_bus_message_read_array(message, 'y', &d, &sz);
153 if (r < 0)
154 return r;
155 if (sz != FAMILY_ADDRESS_SIZE(family))
156 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
157
158 if (!dns_server_address_valid(family, d))
159 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
160
161 r = sd_bus_message_exit_container(message);
162 if (r < 0)
163 return r;
164
165 if (!GREEDY_REALLOC(dns, allocated, n+1))
166 return -ENOMEM;
167
168 dns[n].family = family;
169 memcpy(&dns[n].address, d, sz);
170 n++;
171 }
172
173 r = sd_bus_message_exit_container(message);
174 if (r < 0)
175 return r;
176
177 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
178 "org.freedesktop.network1.set-dns-servers",
179 NULL, true, UID_INVALID,
180 &l->manager->polkit_registry, error);
181 if (r < 0)
182 return r;
183 if (r == 0)
184 return 1; /* Polkit will call us back */
185
186 free_and_replace(l->dns, dns);
187 l->n_dns = n;
188
189 (void) link_dirty(l);
190
191 return sd_bus_reply_method_return(message, NULL);
192 }
193
194 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
195 _cleanup_(ordered_set_freep) OrderedSet *search_domains = NULL, *route_domains = NULL;
196 Link *l = userdata;
197 int r;
198
199 assert(message);
200 assert(l);
201
202 r = verify_managed_link(l, error);
203 if (r < 0)
204 return r;
205
206 r = sd_bus_message_enter_container(message, 'a', "(sb)");
207 if (r < 0)
208 return r;
209
210 for (;;) {
211 _cleanup_free_ char *str = NULL;
212 OrderedSet **domains;
213 const char *name;
214 int route_only;
215
216 r = sd_bus_message_read(message, "(sb)", &name, &route_only);
217 if (r < 0)
218 return r;
219 if (r == 0)
220 break;
221
222 r = dns_name_is_valid(name);
223 if (r < 0)
224 return r;
225 if (r == 0)
226 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
227 if (!route_only && dns_name_is_root(name))
228 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
229
230 r = dns_name_normalize(name, 0, &str);
231 if (r < 0)
232 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
233
234 domains = route_only ? &route_domains : &search_domains;
235 r = ordered_set_ensure_allocated(domains, &string_hash_ops);
236 if (r < 0)
237 return r;
238
239 r = ordered_set_put(*domains, str);
240 if (r < 0)
241 return r;
242
243 TAKE_PTR(str);
244 }
245
246 r = sd_bus_message_exit_container(message);
247 if (r < 0)
248 return r;
249
250 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
251 "org.freedesktop.network1.set-domains",
252 NULL, true, UID_INVALID,
253 &l->manager->polkit_registry, error);
254 if (r < 0)
255 return r;
256 if (r == 0)
257 return 1; /* Polkit will call us back */
258
259 ordered_set_free_free(l->search_domains);
260 ordered_set_free_free(l->route_domains);
261 l->search_domains = TAKE_PTR(search_domains);
262 l->route_domains = TAKE_PTR(route_domains);
263
264 (void) link_dirty(l);
265
266 return sd_bus_reply_method_return(message, NULL);
267 }
268
269 int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
270 Link *l = userdata;
271 int r, b;
272
273 assert(message);
274 assert(l);
275
276 r = verify_managed_link(l, error);
277 if (r < 0)
278 return r;
279
280 r = sd_bus_message_read(message, "b", &b);
281 if (r < 0)
282 return r;
283
284 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
285 "org.freedesktop.network1.set-default-route",
286 NULL, true, UID_INVALID,
287 &l->manager->polkit_registry, error);
288 if (r < 0)
289 return r;
290 if (r == 0)
291 return 1; /* Polkit will call us back */
292
293 if (l->dns_default_route != b) {
294 l->dns_default_route = b;
295 (void) link_dirty(l);
296 }
297
298 return sd_bus_reply_method_return(message, NULL);
299 }
300
301 int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
302 Link *l = userdata;
303 ResolveSupport mode;
304 const char *llmnr;
305 int r;
306
307 assert(message);
308 assert(l);
309
310 r = verify_managed_link(l, error);
311 if (r < 0)
312 return r;
313
314 r = sd_bus_message_read(message, "s", &llmnr);
315 if (r < 0)
316 return r;
317
318 if (isempty(llmnr))
319 mode = RESOLVE_SUPPORT_YES;
320 else {
321 mode = resolve_support_from_string(llmnr);
322 if (mode < 0)
323 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
324 }
325
326 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
327 "org.freedesktop.network1.set-llmnr",
328 NULL, true, UID_INVALID,
329 &l->manager->polkit_registry, error);
330 if (r < 0)
331 return r;
332 if (r == 0)
333 return 1; /* Polkit will call us back */
334
335 if (l->llmnr != mode) {
336 l->llmnr = mode;
337 (void) link_dirty(l);
338 }
339
340 return sd_bus_reply_method_return(message, NULL);
341 }
342
343 int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
344 Link *l = userdata;
345 ResolveSupport mode;
346 const char *mdns;
347 int r;
348
349 assert(message);
350 assert(l);
351
352 r = verify_managed_link(l, error);
353 if (r < 0)
354 return r;
355
356 r = sd_bus_message_read(message, "s", &mdns);
357 if (r < 0)
358 return r;
359
360 if (isempty(mdns))
361 mode = RESOLVE_SUPPORT_NO;
362 else {
363 mode = resolve_support_from_string(mdns);
364 if (mode < 0)
365 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
366 }
367
368 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
369 "org.freedesktop.network1.set-mdns",
370 NULL, true, UID_INVALID,
371 &l->manager->polkit_registry, error);
372 if (r < 0)
373 return r;
374 if (r == 0)
375 return 1; /* Polkit will call us back */
376
377 if (l->mdns != mode) {
378 l->mdns = mode;
379 (void) link_dirty(l);
380 }
381
382 return sd_bus_reply_method_return(message, NULL);
383 }
384
385 int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error) {
386 Link *l = userdata;
387 const char *dns_over_tls;
388 DnsOverTlsMode mode;
389 int r;
390
391 assert(message);
392 assert(l);
393
394 r = verify_managed_link(l, error);
395 if (r < 0)
396 return r;
397
398 r = sd_bus_message_read(message, "s", &dns_over_tls);
399 if (r < 0)
400 return r;
401
402 if (isempty(dns_over_tls))
403 mode = _DNS_OVER_TLS_MODE_INVALID;
404 else {
405 mode = dns_over_tls_mode_from_string(dns_over_tls);
406 if (mode < 0)
407 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
408 }
409
410 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
411 "org.freedesktop.network1.set-dns-over-tls",
412 NULL, true, UID_INVALID,
413 &l->manager->polkit_registry, error);
414 if (r < 0)
415 return r;
416 if (r == 0)
417 return 1; /* Polkit will call us back */
418
419 if (l->dns_over_tls_mode != mode) {
420 l->dns_over_tls_mode = mode;
421 (void) link_dirty(l);
422 }
423
424 return sd_bus_reply_method_return(message, NULL);
425 }
426
427 int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
428 Link *l = userdata;
429 const char *dnssec;
430 DnssecMode mode;
431 int r;
432
433 assert(message);
434 assert(l);
435
436 r = verify_managed_link(l, error);
437 if (r < 0)
438 return r;
439
440 r = sd_bus_message_read(message, "s", &dnssec);
441 if (r < 0)
442 return r;
443
444 if (isempty(dnssec))
445 mode = _DNSSEC_MODE_INVALID;
446 else {
447 mode = dnssec_mode_from_string(dnssec);
448 if (mode < 0)
449 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
450 }
451
452 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
453 "org.freedesktop.network1.set-dnssec",
454 NULL, true, UID_INVALID,
455 &l->manager->polkit_registry, error);
456 if (r < 0)
457 return r;
458 if (r == 0)
459 return 1; /* Polkit will call us back */
460
461 if (l->dnssec_mode != mode) {
462 l->dnssec_mode = mode;
463 (void) link_dirty(l);
464 }
465
466 return sd_bus_reply_method_return(message, NULL);
467 }
468
469 int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
470 _cleanup_set_free_free_ Set *ns = NULL;
471 _cleanup_strv_free_ char **ntas = NULL;
472 Link *l = userdata;
473 int r;
474 char **i;
475
476 assert(message);
477 assert(l);
478
479 r = verify_managed_link(l, error);
480 if (r < 0)
481 return r;
482
483 r = sd_bus_message_read_strv(message, &ntas);
484 if (r < 0)
485 return r;
486
487 STRV_FOREACH(i, ntas) {
488 r = dns_name_is_valid(*i);
489 if (r < 0)
490 return r;
491 if (r == 0)
492 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
493 }
494
495 ns = set_new(&dns_name_hash_ops);
496 if (!ns)
497 return -ENOMEM;
498
499 STRV_FOREACH(i, ntas) {
500 r = set_put_strdup(ns, *i);
501 if (r < 0)
502 return r;
503 }
504
505 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
506 "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
507 NULL, true, UID_INVALID,
508 &l->manager->polkit_registry, error);
509 if (r < 0)
510 return r;
511 if (r == 0)
512 return 1; /* Polkit will call us back */
513
514 set_free_free(l->dnssec_negative_trust_anchors);
515 l->dnssec_negative_trust_anchors = TAKE_PTR(ns);
516
517 (void) link_dirty(l);
518
519 return sd_bus_reply_method_return(message, NULL);
520 }
521
522 int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error) {
523 Link *l = userdata;
524 int r;
525
526 assert(message);
527 assert(l);
528
529 r = verify_managed_link(l, error);
530 if (r < 0)
531 return r;
532
533 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
534 "org.freedesktop.network1.revert-ntp",
535 NULL, true, UID_INVALID,
536 &l->manager->polkit_registry, error);
537 if (r < 0)
538 return r;
539 if (r == 0)
540 return 1; /* Polkit will call us back */
541
542 link_ntp_settings_clear(l);
543 (void) link_dirty(l);
544
545 return sd_bus_reply_method_return(message, NULL);
546 }
547
548 int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
549 Link *l = userdata;
550 int r;
551
552 assert(message);
553 assert(l);
554
555 r = verify_managed_link(l, error);
556 if (r < 0)
557 return r;
558
559 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
560 "org.freedesktop.network1.revert-dns",
561 NULL, true, UID_INVALID,
562 &l->manager->polkit_registry, error);
563 if (r < 0)
564 return r;
565 if (r == 0)
566 return 1; /* Polkit will call us back */
567
568 link_dns_settings_clear(l);
569 (void) link_dirty(l);
570
571 return sd_bus_reply_method_return(message, NULL);
572 }
573
574 int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
575 Link *l = userdata;
576 int r;
577
578 assert(l);
579
580 if (!l->network)
581 return sd_bus_error_setf(error, BUS_ERROR_UNMANAGED_INTERFACE,
582 "Interface %s is not managed by systemd-networkd",
583 l->ifname);
584
585 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
586 "org.freedesktop.network1.renew",
587 NULL, true, UID_INVALID,
588 &l->manager->polkit_registry, error);
589 if (r < 0)
590 return r;
591 if (r == 0)
592 return 1; /* Polkit will call us back */
593
594 if (l->dhcp_client) {
595 r = sd_dhcp_client_send_renew(l->dhcp_client);
596 if (r < 0)
597 return r;
598 }
599
600 return sd_bus_reply_method_return(message, NULL);
601 }
602
603 int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error) {
604 Link *l = userdata;
605 int r;
606
607 assert(message);
608 assert(l);
609
610 r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
611 "org.freedesktop.network1.reconfigure",
612 NULL, true, UID_INVALID,
613 &l->manager->polkit_registry, error);
614 if (r < 0)
615 return r;
616 if (r == 0)
617 return 1; /* Polkit will call us back */
618
619 r = link_reconfigure(l, true);
620 if (r < 0)
621 return r;
622
623 return sd_bus_reply_method_return(message, NULL);
624 }
625
626 const sd_bus_vtable link_vtable[] = {
627 SD_BUS_VTABLE_START(0),
628
629 SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
630 SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Link, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
631 SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Link, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
632 SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
633 SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),
634
635 SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
636 SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
637 SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
638 SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
639 SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
640 SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, SD_BUS_VTABLE_UNPRIVILEGED),
641 SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED),
642 SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, SD_BUS_VTABLE_UNPRIVILEGED),
643 SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED),
644 SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
645 SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
646 SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
647 SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
648
649 SD_BUS_VTABLE_END
650 };
651
652 char *link_bus_path(Link *link) {
653 _cleanup_free_ char *ifindex = NULL;
654 char *p;
655 int r;
656
657 assert(link);
658 assert(link->ifindex > 0);
659
660 if (asprintf(&ifindex, "%d", link->ifindex) < 0)
661 return NULL;
662
663 r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex, &p);
664 if (r < 0)
665 return NULL;
666
667 return p;
668 }
669
670 int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
671 _cleanup_strv_free_ char **l = NULL;
672 Manager *m = userdata;
673 unsigned c = 0;
674 Link *link;
675 Iterator i;
676
677 assert(bus);
678 assert(path);
679 assert(m);
680 assert(nodes);
681
682 l = new0(char*, hashmap_size(m->links) + 1);
683 if (!l)
684 return -ENOMEM;
685
686 HASHMAP_FOREACH(link, m->links, i) {
687 char *p;
688
689 p = link_bus_path(link);
690 if (!p)
691 return -ENOMEM;
692
693 l[c++] = p;
694 }
695
696 l[c] = NULL;
697 *nodes = TAKE_PTR(l);
698
699 return 1;
700 }
701
702 int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
703 _cleanup_free_ char *identifier = NULL;
704 Manager *m = userdata;
705 Link *link;
706 int ifindex, r;
707
708 assert(bus);
709 assert(path);
710 assert(interface);
711 assert(m);
712 assert(found);
713
714 r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier);
715 if (r <= 0)
716 return 0;
717
718 ifindex = parse_ifindex(identifier);
719 if (ifindex < 0)
720 return 0;
721
722 r = link_get(m, ifindex, &link);
723 if (r < 0)
724 return 0;
725
726 *found = link;
727
728 return 1;
729 }
730
731 int link_send_changed_strv(Link *link, char **properties) {
732 _cleanup_free_ char *p = NULL;
733
734 assert(link);
735 assert(link->manager);
736 assert(properties);
737
738 if (!link->manager->bus)
739 return 0;
740
741 p = link_bus_path(link);
742 if (!p)
743 return -ENOMEM;
744
745 return sd_bus_emit_properties_changed_strv(
746 link->manager->bus,
747 p,
748 "org.freedesktop.network1.Link",
749 properties);
750 }
751
752 int link_send_changed(Link *link, const char *property, ...) {
753 char **properties;
754
755 properties = strv_from_stdarg_alloca(property);
756
757 return link_send_changed_strv(link, properties);
758 }