chiark / gitweb /
networkd: make sure Links and Networks are freed
[elogind.git] / src / network / networkd-manager.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 "path-util.h"
23 #include "networkd.h"
24 #include "libudev-private.h"
25
26 int manager_new(Manager **ret) {
27         _cleanup_manager_free_ Manager *m = NULL;
28         int r;
29
30         m = new0(Manager, 1);
31         if (!m)
32                 return -ENOMEM;
33
34         r = sd_event_default(&m->event);
35         if (r < 0)
36                 return r;
37
38         r = sd_rtnl_open(0, &m->rtnl);
39         if (r < 0)
40                 return r;
41
42         m->udev = udev_new();
43         if (!m->udev)
44                 return -ENOMEM;
45
46         m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
47         if (!m->udev_monitor)
48                 return -ENOMEM;
49
50         m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
51         if (!m->links)
52                 return -ENOMEM;
53
54         LIST_HEAD_INIT(m->networks);
55
56         m->network_dirs = strv_new("/etc/systemd/network/",
57                         "/run/systemd/network/",
58                         "/usr/lib/systemd/network",
59 #ifdef HAVE_SPLIT_USER
60                         "/lib/systemd/network",
61 #endif
62                         NULL);
63         if (!m->network_dirs)
64                 return -ENOMEM;
65
66         if (!path_strv_canonicalize_uniq(m->network_dirs))
67                 return -ENOMEM;
68
69         *ret = m;
70         m = NULL;
71
72         return 0;
73 }
74
75 void manager_free(Manager *m) {
76         Network *network;
77         Link *link;
78
79         udev_monitor_unref(m->udev_monitor);
80         udev_unref(m->udev);
81         sd_event_source_unref(m->udev_event_source);
82         sd_event_unref(m->event);
83
84         while ((network = m->networks))
85                 network_free(network);
86
87         while ((link = hashmap_first(m->links)))
88                 link_free(link);
89         hashmap_free(m->links);
90
91         strv_free(m->network_dirs);
92         sd_rtnl_unref(m->rtnl);
93
94         free(m);
95 }
96
97 static int manager_process_link(Manager *m, struct udev_device *device) {
98         Link *link;
99         int r;
100
101         if (streq_ptr(udev_device_get_action(device), "remove")) {
102                 uint64_t ifindex;
103
104                 ifindex = udev_device_get_ifindex(device);
105                 link = hashmap_get(m->links, &ifindex);
106                 if (!link)
107                         return 0;
108
109                 link_free(link);
110         } else {
111                 r = link_add(m, device);
112                 if (r < 0) {
113                         log_error("Could not handle link %s: %s",
114                                         udev_device_get_sysname(device),
115                                         strerror(-r));
116                 }
117         }
118
119         return 0;
120 }
121
122 int manager_udev_enumerate_links(Manager *m) {
123         struct udev_list_entry *item = NULL, *first = NULL;
124         struct udev_enumerate *e;
125         int r;
126
127         assert(m);
128
129         e = udev_enumerate_new(m->udev);
130         if (!e) {
131                 r = -ENOMEM;
132                 goto finish;
133         }
134
135         r = udev_enumerate_add_match_subsystem(e, "net");
136         if (r < 0)
137                 goto finish;
138
139         r = udev_enumerate_add_match_tag(e, "systemd-networkd");
140         if (r < 0)
141                 goto finish;
142
143         r = udev_enumerate_scan_devices(e);
144         if (r < 0)
145                 goto finish;
146
147         first = udev_enumerate_get_list_entry(e);
148         udev_list_entry_foreach(item, first) {
149                 struct udev_device *d;
150                 int k;
151
152                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
153                 if (!d) {
154                         r = -ENOMEM;
155                         goto finish;
156                 }
157
158                 k = manager_process_link(m, d);
159                 udev_device_unref(d);
160
161                 if (k < 0)
162                         r = k;
163         }
164
165 finish:
166         if (e)
167                 udev_enumerate_unref(e);
168
169         return r;
170 }
171
172 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
173         Manager *m = userdata;
174         struct udev_monitor *monitor = m->udev_monitor;
175         struct udev_device *device;
176         int r;
177
178         device = udev_monitor_receive_device(monitor);
179         if (!device)
180                 return -ENOMEM;
181
182         r = manager_process_link(m, device);
183         if (r < 0)
184                 return r;
185
186         udev_device_unref(device);
187
188         return 0;
189 }
190
191 int manager_udev_listen(Manager *m) {
192         int r;
193
194         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
195         if (r < 0) {
196                 log_error("Could not add udev monitor filter: %s", strerror(-r));
197                 return r;
198         }
199
200         r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd-networkd");
201         if (r < 0) {
202                 log_error("Could not add udev monitor filter: %s", strerror(-r));
203                 return r;
204         }
205
206         r = udev_monitor_enable_receiving(m->udev_monitor);
207         if (r < 0) {
208                 log_error("Could not enable udev monitor");
209                 return r;
210         }
211
212         r = sd_event_add_io(m->event,
213                         udev_monitor_get_fd(m->udev_monitor),
214                         EPOLLIN, manager_dispatch_link_udev,
215                         m, &m->udev_event_source);
216         if (r < 0)
217                 return r;
218
219         return 0;
220 }
221
222 int manager_rtnl_listen(Manager *m) {
223         int r;
224
225         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
226         if (r < 0)
227                 return r;
228
229         return 0;
230 }