chiark / gitweb /
networkd: netdev - split NetDev struct into per-kind structs
[elogind.git] / src / network / networkd-netdev.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <net/if.h>
23
24 #include "networkd-netdev.h"
25 #include "network-internal.h"
26 #include "path-util.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
29 #include "list.h"
30 #include "siphash24.h"
31
32 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
33         [NETDEV_KIND_BRIDGE] = &bridge_vtable,
34         [NETDEV_KIND_BOND] = &bond_vtable,
35         [NETDEV_KIND_VLAN] = &vlan_vtable,
36         [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
37         [NETDEV_KIND_VXLAN] = &vxlan_vtable,
38         [NETDEV_KIND_IPIP] = &ipip_vtable,
39         [NETDEV_KIND_GRE] = &gre_vtable,
40         [NETDEV_KIND_SIT] = &sit_vtable,
41         [NETDEV_KIND_VTI] = &vti_vtable,
42         [NETDEV_KIND_VETH] = &veth_vtable,
43         [NETDEV_KIND_DUMMY] = &dummy_vtable,
44         [NETDEV_KIND_TUN] = &tun_vtable,
45         [NETDEV_KIND_TAP] = &tap_vtable,
46 };
47
48 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
49         [NETDEV_KIND_BRIDGE] = "bridge",
50         [NETDEV_KIND_BOND] = "bond",
51         [NETDEV_KIND_VLAN] = "vlan",
52         [NETDEV_KIND_MACVLAN] = "macvlan",
53         [NETDEV_KIND_VXLAN] = "vxlan",
54         [NETDEV_KIND_IPIP] = "ipip",
55         [NETDEV_KIND_GRE] = "gre",
56         [NETDEV_KIND_SIT] = "sit",
57         [NETDEV_KIND_VETH] = "veth",
58         [NETDEV_KIND_VTI] = "vti",
59         [NETDEV_KIND_DUMMY] = "dummy",
60         [NETDEV_KIND_TUN] = "tun",
61         [NETDEV_KIND_TAP] = "tap",
62 };
63
64 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
65 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
66
67
68 static void netdev_cancel_callbacks(NetDev *netdev) {
69         _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
70         netdev_join_callback *callback;
71
72         if (!netdev)
73                 return;
74
75         rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
76
77         while ((callback = netdev->callbacks)) {
78                 if (m) {
79                         assert(callback->link);
80                         assert(callback->callback);
81                         assert(netdev->manager);
82                         assert(netdev->manager->rtnl);
83
84                         callback->callback(netdev->manager->rtnl, m, link);
85                 }
86
87                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
88                 free(callback);
89         }
90 }
91
92 static void netdev_free(NetDev *netdev) {
93         if (!netdev)
94                 return;
95
96         netdev_cancel_callbacks(netdev);
97
98         if (netdev->ifname)
99                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
100
101         free(netdev->filename);
102
103         free(netdev->description);
104         free(netdev->ifname);
105         free(netdev->mac);
106
107         condition_free_list(netdev->match_host);
108         condition_free_list(netdev->match_virt);
109         condition_free_list(netdev->match_kernel);
110         condition_free_list(netdev->match_arch);
111
112         if (NETDEV_VTABLE(netdev) &&
113             NETDEV_VTABLE(netdev)->done)
114                 NETDEV_VTABLE(netdev)->done(netdev);
115
116         free(netdev);
117 }
118
119 NetDev *netdev_unref(NetDev *netdev) {
120         if (netdev && (-- netdev->n_ref <= 0))
121                 netdev_free(netdev);
122
123         return NULL;
124 }
125
126 NetDev *netdev_ref(NetDev *netdev) {
127         if (netdev)
128                 assert_se(++ netdev->n_ref >= 2);
129
130         return netdev;
131 }
132
133 void netdev_drop(NetDev *netdev) {
134         if (!netdev || netdev->state == NETDEV_STATE_LINGER)
135                 return;
136
137         netdev->state = NETDEV_STATE_LINGER;
138
139         log_debug_netdev(netdev, "netdev removed");
140
141         netdev_cancel_callbacks(netdev);
142
143         netdev_unref(netdev);
144
145         return;
146 }
147
148 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
149         NetDev *netdev;
150
151         assert(manager);
152         assert(name);
153         assert(ret);
154
155         netdev = hashmap_get(manager->netdevs, name);
156         if (!netdev) {
157                 *ret = NULL;
158                 return -ENOENT;
159         }
160
161         *ret = netdev;
162
163         return 0;
164 }
165
166 static int netdev_enter_failed(NetDev *netdev) {
167         netdev->state = NETDEV_STATE_FAILED;
168
169         return 0;
170 }
171
172 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
173         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
174         int r;
175
176         assert(netdev);
177         assert(netdev->state == NETDEV_STATE_READY);
178         assert(netdev->manager);
179         assert(netdev->manager->rtnl);
180         assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
181         assert(link);
182         assert(callback);
183
184         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
185                                      RTM_SETLINK, link->ifindex);
186         if (r < 0) {
187                 log_error_netdev(netdev,
188                                  "Could not allocate RTM_SETLINK message: %s",
189                                  strerror(-r));
190                 return r;
191         }
192
193         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
194         if (r < 0) {
195                 log_error_netdev(netdev,
196                                  "Could not append IFLA_MASTER attribute: %s",
197                                  strerror(-r));
198                 return r;
199         }
200
201         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
202         if (r < 0) {
203                 log_error_netdev(netdev,
204                                  "Could not send rtnetlink message: %s",
205                                  strerror(-r));
206                 return r;
207         }
208
209         link_ref(link);
210
211         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
212
213         return 0;
214 }
215
216 static int netdev_enter_ready(NetDev *netdev) {
217         netdev_join_callback *callback, *callback_next;
218         int r;
219
220         assert(netdev);
221         assert(netdev->ifname);
222
223         if (netdev->state != NETDEV_STATE_CREATING)
224                 return 0;
225
226         netdev->state = NETDEV_STATE_READY;
227
228         log_info_netdev(netdev, "netdev ready");
229
230         LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
231                 /* enslave the links that were attempted to be enslaved before the
232                  * link was ready */
233                 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
234                 if (r < 0)
235                         return r;
236
237                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
238                 link_unref(callback->link);
239                 free(callback);
240         }
241
242         return 0;
243 }
244
245 /* callback for netdev's created without a backing Link */
246 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
247         _cleanup_netdev_unref_ NetDev *netdev = userdata;
248         int r;
249
250         assert(netdev->state != _NETDEV_STATE_INVALID);
251
252         r = sd_rtnl_message_get_errno(m);
253         if (r == -EEXIST)
254                 log_debug_netdev(netdev, "netdev exists, using existing");
255         else if (r < 0) {
256                 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
257                 netdev_drop(netdev);
258
259                 return 1;
260         }
261
262         log_debug_netdev(netdev, "created");
263
264         return 1;
265 }
266
267 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
268         int r;
269
270         assert(netdev);
271         assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
272
273         if (netdev->state == NETDEV_STATE_READY) {
274                 r = netdev_enslave_ready(netdev, link, callback);
275                 if (r < 0)
276                         return r;
277         } else {
278                 /* the netdev is not yet read, save this request for when it is*/
279                 netdev_join_callback *cb;
280
281                 cb = new0(netdev_join_callback, 1);
282                 if (!cb)
283                         return log_oom();
284
285                 cb->callback = callback;
286                 cb->link = link;
287                 link_ref(link);
288
289                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
290
291                 log_debug_netdev(netdev, "will enslave '%s', when reday",
292                                  link->ifname);
293         }
294
295         return 0;
296 }
297
298 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
299         uint16_t type;
300         const char *kind;
301         const char *received_kind;
302         const char *received_name;
303         int r, ifindex;
304
305         assert(netdev);
306         assert(message);
307
308         r = sd_rtnl_message_get_type(message, &type);
309         if (r < 0) {
310                 log_error_netdev(netdev, "Could not get rtnl message type");
311                 return r;
312         }
313
314         if (type != RTM_NEWLINK) {
315                 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
316                 return -EINVAL;
317         }
318
319         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
320         if (r < 0) {
321                 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
322                 netdev_enter_failed(netdev);
323                 return r;
324         } else if (ifindex <= 0) {
325                 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
326                 netdev_enter_failed(netdev);
327                 return r;
328         }
329
330         if (netdev->ifindex > 0) {
331                 if (netdev->ifindex != ifindex) {
332                         log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
333                                          ifindex, netdev->ifindex);
334                         netdev_enter_failed(netdev);
335                         return -EEXIST;
336                 } else
337                         /* ifindex already set to the same for this netdev */
338                         return 0;
339         }
340
341         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
342         if (r < 0) {
343                 log_error_netdev(netdev, "Could not get IFNAME");
344                 return r;
345         }
346
347         if (!streq(netdev->ifname, received_name)) {
348                 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
349                                  received_name);
350                 netdev_enter_failed(netdev);
351                 return r;
352         }
353
354         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
355         if (r < 0) {
356                 log_error_netdev(netdev, "Could not get LINKINFO");
357                 return r;
358         }
359
360         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
361         if (r < 0) {
362                 log_error_netdev(netdev, "Could not get KIND");
363                 return r;
364         }
365
366         r = sd_rtnl_message_exit_container(message);
367         if (r < 0) {
368                 log_error_netdev(netdev, "Could not exit container");
369                 return r;
370         }
371
372         if (netdev->kind == NETDEV_KIND_TAP)
373                 /* the kernel does not distinguish between tun and tap */
374                 kind = "tun";
375         else {
376                 kind = netdev_kind_to_string(netdev->kind);
377                 if (!kind) {
378                         log_error_netdev(netdev, "Could not get kind");
379                         netdev_enter_failed(netdev);
380                         return -EINVAL;
381                 }
382         }
383
384         if (!streq(kind, received_kind)) {
385                 log_error_netdev(netdev,
386                                  "Received newlink with wrong KIND %s, "
387                                  "expected %s", received_kind, kind);
388                 netdev_enter_failed(netdev);
389                 return r;
390         }
391
392         netdev->ifindex = ifindex;
393
394         log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex);
395
396         netdev_enter_ready(netdev);
397
398         return 0;
399 }
400
401 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
402
403 int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
404         _cleanup_free_ struct ether_addr *mac = NULL;
405         uint8_t result[8];
406         size_t l, sz;
407         uint8_t *v;
408         int r;
409
410         assert(ifname);
411         assert(ret);
412
413         mac = new0(struct ether_addr, 1);
414         if (!mac)
415                 return -ENOMEM;
416
417         l = strlen(ifname);
418         sz = sizeof(sd_id128_t) + l;
419         v = alloca(sz);
420
421         /* fetch some persistent data unique to the machine */
422         r = sd_id128_get_machine((sd_id128_t*) v);
423         if (r < 0)
424                 return r;
425
426         /* combine with some data unique (on this machine) to this
427          * netdev */
428         memcpy(v + sizeof(sd_id128_t), ifname, l);
429
430         /* Let's hash the host machine ID plus the container name. We
431          * use a fixed, but originally randomly created hash key here. */
432         siphash24(result, v, sz, HASH_KEY.bytes);
433
434         assert_cc(ETH_ALEN <= sizeof(result));
435         memcpy(mac->ether_addr_octet, result, ETH_ALEN);
436
437         /* see eth_random_addr in the kernel */
438         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
439         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
440
441         *ret = mac;
442         mac = NULL;
443
444         return 0;
445 }
446
447 static int netdev_create(NetDev *netdev, Link *link) {
448         int r;
449
450         assert(netdev);
451
452         /* create netdev */
453         if (NETDEV_VTABLE(netdev)->create) {
454                 r = NETDEV_VTABLE(netdev)->create(netdev);
455                 if (r < 0)
456                         return r;
457
458                 log_debug_netdev(netdev, "created");
459         } else {
460                 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
461
462                 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
463                 if (r < 0) {
464                         log_error_netdev(netdev,
465                                          "Could not allocate RTM_NEWLINK message: %s",
466                                          strerror(-r));
467                         return r;
468                 }
469
470                 r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname);
471                 if (r < 0) {
472                         log_error_netdev(netdev,
473                                          "Could not append IFLA_IFNAME, attribute: %s",
474                                          strerror(-r));
475                         return r;
476                 }
477
478                 if (netdev->mac) {
479                         r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
480                         if (r < 0) {
481                                 log_error_netdev(netdev,
482                                                  "Could not append IFLA_ADDRESS attribute: %s",
483                                                  strerror(-r));
484                             return r;
485                         }
486                 }
487
488                 if (netdev->mtu) {
489                         r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu);
490                         if (r < 0) {
491                                 log_error_netdev(netdev,
492                                                  "Could not append IFLA_MTU attribute: %s",
493                                                  strerror(-r));
494                                 return r;
495                         }
496                 }
497
498                 if (link) {
499                         r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex);
500                         if (r < 0) {
501                                 log_error_netdev(netdev,
502                                                  "Colud not append IFLA_LINK attribute: %s",
503                                                  strerror(-r));
504                                 return r;
505                         }
506                 }
507
508                 r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
509                 if (r < 0) {
510                         log_error_netdev(netdev,
511                                          "Could not append IFLA_LINKINFO attribute: %s",
512                                          strerror(-r));
513                         return r;
514                 }
515
516                 r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA,
517                                                          netdev_kind_to_string(netdev->kind));
518                 if (r < 0) {
519                         log_error_netdev(netdev,
520                                          "Could not append IFLA_INFO_DATA attribute: %s",
521                                          strerror(-r));
522                         return r;
523                 }
524
525                 if (NETDEV_VTABLE(netdev)->fill_message_create) {
526                         r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
527                         if (r < 0)
528                                 return r;
529                 }
530
531                 r = sd_rtnl_message_close_container(m);
532                 if (r < 0) {
533                         log_error_netdev(netdev,
534                                          "Could not append IFLA_LINKINFO attribute: %s",
535                                          strerror(-r));
536                         return r;
537                 }
538
539                 r = sd_rtnl_message_close_container(m);
540                 if (r < 0) {
541                         log_error_netdev(netdev,
542                                          "Could not append IFLA_LINKINFO attribute: %s",
543                                          strerror(-r));
544                         return r;
545                 }
546
547
548                 r = sd_rtnl_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
549                 if (r < 0) {
550                         log_error_netdev(netdev,
551                                          "Could not send rtnetlink message: %s", strerror(-r));
552                         return r;
553                 }
554
555                 netdev_ref(netdev);
556
557                 netdev->state = NETDEV_STATE_CREATING;
558
559                 log_debug_netdev(netdev, "creating");
560         }
561
562         return 0;
563 }
564
565 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
566 int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
567         int r;
568
569         assert(netdev);
570         assert(netdev->manager);
571         assert(netdev->manager->rtnl);
572         assert(NETDEV_VTABLE(netdev));
573
574         switch (NETDEV_VTABLE(netdev)->create_type) {
575         case NETDEV_CREATE_MASTER:
576                 r = netdev_enslave(netdev, link, callback);
577                 if (r < 0)
578                         return r;
579
580                 break;
581         case NETDEV_CREATE_STACKED:
582                 r = netdev_create(netdev, link);
583                 if (r < 0)
584                         return r;
585
586                 break;
587         default:
588                 assert_not_reached("Can not join independent netdev");
589         }
590
591         return 0;
592 }
593
594 static int netdev_load_one(Manager *manager, const char *filename) {
595         _cleanup_netdev_unref_ NetDev *netdev = NULL;
596         _cleanup_free_ NetDev *netdev_raw = NULL;
597         _cleanup_fclose_ FILE *file = NULL;
598         int r;
599
600         assert(manager);
601         assert(filename);
602
603         file = fopen(filename, "re");
604         if (!file) {
605                 if (errno == ENOENT)
606                         return 0;
607                 else
608                         return -errno;
609         }
610
611         if (null_or_empty_fd(fileno(file))) {
612                 log_debug("Skipping empty file: %s", filename);
613                 return 0;
614         }
615
616         netdev_raw = new0(NetDev, 1);
617         if (!netdev_raw)
618                 return log_oom();
619
620         netdev_raw->kind = _NETDEV_KIND_INVALID;
621
622         r = config_parse(NULL, filename, file,
623                          "Match\0NetDev\0",
624                          config_item_perf_lookup, network_netdev_gperf_lookup,
625                          true, false, true, netdev_raw);
626         if (r < 0)
627                 return r;
628
629         r = fseek(file, 0, SEEK_SET);
630         if (r < 0)
631                 return -errno;
632
633         /* skip out early if configuration does not match the environment */
634         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
635                              netdev_raw->match_host, netdev_raw->match_virt,
636                              netdev_raw->match_kernel, netdev_raw->match_arch,
637                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
638                 return 0;
639
640         if (!NETDEV_VTABLE(netdev_raw)) {
641                 log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
642                 return 0;
643         }
644
645         if (!netdev_raw->ifname) {
646                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
647                 return 0;
648         }
649
650         netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
651         if (!netdev)
652                 return log_oom();
653
654         netdev->n_ref = 1;
655         netdev->manager = manager;
656         netdev->state = _NETDEV_STATE_INVALID;
657         netdev->kind = netdev_raw->kind;
658         netdev->ifname = netdev_raw->ifname;
659
660         if (NETDEV_VTABLE(netdev)->init)
661                 NETDEV_VTABLE(netdev)->init(netdev);
662
663         r = config_parse(NULL, filename, file,
664                          NETDEV_VTABLE(netdev)->sections,
665                          config_item_perf_lookup, network_netdev_gperf_lookup,
666                          false, false, false, netdev);
667         if (r < 0)
668                 return r;
669
670         /* verify configuration */
671         if (NETDEV_VTABLE(netdev)->config_verify) {
672                 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
673                 if (r < 0)
674                         return 0;
675         }
676
677         netdev->filename = strdup(filename);
678         if (!netdev->filename)
679                 return log_oom();
680
681         if (!netdev->mac) {
682                 r = netdev_get_mac(netdev->ifname, &netdev->mac);
683                 if (r < 0) {
684                         log_error("Failed to generate predictable MAC address for %s",
685                                   netdev->ifname);
686                         return r;
687                 }
688         }
689
690         r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
691         if (r < 0)
692                 return r;
693
694         LIST_HEAD_INIT(netdev->callbacks);
695
696         log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
697
698         switch (NETDEV_VTABLE(netdev)->create_type) {
699         case NETDEV_CREATE_MASTER:
700         case NETDEV_CREATE_INDEPENDENT:
701                 r = netdev_create(netdev, NULL);
702                 if (r < 0)
703                         return r;
704
705                 break;
706         default:
707                 break;
708         }
709
710         netdev = NULL;
711
712         return 0;
713 }
714
715 int netdev_load(Manager *manager) {
716         NetDev *netdev;
717         char **files, **f;
718         int r;
719
720         assert(manager);
721
722         while ((netdev = hashmap_first(manager->netdevs)))
723                 netdev_unref(netdev);
724
725         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
726         if (r < 0) {
727                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
728                 return r;
729         }
730
731         STRV_FOREACH_BACKWARDS(f, files) {
732                 r = netdev_load_one(manager, *f);
733                 if (r < 0)
734                         return r;
735         }
736
737         strv_free(files);
738
739         return 0;
740 }