chiark / gitweb /
724e5e5a47f654c27103115d710ffd400907b7d3
[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 #include "udev-util.h"
26
27 int manager_new(Manager **ret) {
28         _cleanup_manager_free_ Manager *m = NULL;
29         int r;
30
31         m = new0(Manager, 1);
32         if (!m)
33                 return -ENOMEM;
34
35         r = sd_event_default(&m->event);
36         if (r < 0)
37                 return r;
38
39         sd_event_set_watchdog(m->event, true);
40
41         r = sd_rtnl_open(RTMGRP_LINK | RTMGRP_IPV4_IFADDR, &m->rtnl);
42         if (r < 0)
43                 return r;
44
45         m->udev = udev_new();
46         if (!m->udev)
47                 return -ENOMEM;
48
49         m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
50         if (!m->udev_monitor)
51                 return -ENOMEM;
52
53         m->links = hashmap_new(uint64_hash_func, uint64_compare_func);
54         if (!m->links)
55                 return -ENOMEM;
56
57         m->bridges = hashmap_new(string_hash_func, string_compare_func);
58         if (!m->bridges)
59                 return -ENOMEM;
60
61         LIST_HEAD_INIT(m->networks);
62
63         m->network_dirs = strv_new("/etc/systemd/network/",
64                         "/run/systemd/network/",
65                         "/usr/lib/systemd/network",
66 #ifdef HAVE_SPLIT_USER
67                         "/lib/systemd/network",
68 #endif
69                         NULL);
70         if (!m->network_dirs)
71                 return -ENOMEM;
72
73         if (!path_strv_canonicalize_uniq(m->network_dirs))
74                 return -ENOMEM;
75
76         *ret = m;
77         m = NULL;
78
79         return 0;
80 }
81
82 void manager_free(Manager *m) {
83         Network *network;
84         Bridge *bridge;
85         Link *link;
86
87         udev_monitor_unref(m->udev_monitor);
88         udev_unref(m->udev);
89         sd_event_source_unref(m->udev_event_source);
90         sd_event_unref(m->event);
91
92         while ((network = m->networks))
93                 network_free(network);
94
95         while ((link = hashmap_first(m->links)))
96                 link_free(link);
97         hashmap_free(m->links);
98
99         while ((bridge = hashmap_first(m->bridges)))
100                 bridge_free(bridge);
101         hashmap_free(m->bridges);
102
103         strv_free(m->network_dirs);
104         sd_rtnl_unref(m->rtnl);
105
106         free(m);
107 }
108
109 int manager_load_config(Manager *m) {
110         int r;
111
112         /* update timestamp */
113         paths_check_timestamp(m->network_dirs, &m->network_dirs_ts_usec, true);
114
115         r = bridge_load(m);
116         if (r < 0)
117                 return r;
118
119         r = network_load(m);
120         if (r < 0)
121                 return r;
122
123         return 0;
124 }
125
126 bool manager_should_reload(Manager *m) {
127         return paths_check_timestamp(m->network_dirs, &m->network_dirs_ts_usec, false);
128 }
129
130 static int manager_process_link(Manager *m, struct udev_device *device) {
131         Link *link;
132         int r;
133
134         if (streq_ptr(udev_device_get_action(device), "remove")) {
135                 uint64_t ifindex;
136
137                 log_debug("Link removed: %s", udev_device_get_sysname(device));
138
139                 ifindex = udev_device_get_ifindex(device);
140                 link = hashmap_get(m->links, &ifindex);
141                 if (!link)
142                         return 0;
143
144                 link_free(link);
145         } else {
146                 log_debug("New link: %s", udev_device_get_sysname(device));
147
148                 r = link_add(m, device);
149                 if (r < 0) {
150                         log_error("Could not handle link %s: %s",
151                                         udev_device_get_sysname(device),
152                                         strerror(-r));
153                 }
154         }
155
156         return 0;
157 }
158
159 int manager_udev_enumerate_links(Manager *m) {
160         struct udev_list_entry *item = NULL, *first = NULL;
161         struct udev_enumerate *e;
162         int r;
163
164         assert(m);
165
166         e = udev_enumerate_new(m->udev);
167         if (!e) {
168                 r = -ENOMEM;
169                 goto finish;
170         }
171
172         r = udev_enumerate_add_match_subsystem(e, "net");
173         if (r < 0)
174                 goto finish;
175
176         r = udev_enumerate_scan_devices(e);
177         if (r < 0)
178                 goto finish;
179
180         first = udev_enumerate_get_list_entry(e);
181         udev_list_entry_foreach(item, first) {
182                 struct udev_device *d;
183                 int k;
184
185                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
186                 if (!d) {
187                         r = -ENOMEM;
188                         goto finish;
189                 }
190
191                 k = manager_process_link(m, d);
192                 udev_device_unref(d);
193
194                 if (k < 0)
195                         r = k;
196         }
197
198 finish:
199         if (e)
200                 udev_enumerate_unref(e);
201
202         return r;
203 }
204
205 static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
206         Manager *m = userdata;
207         struct udev_monitor *monitor = m->udev_monitor;
208         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
209
210         device = udev_monitor_receive_device(monitor);
211         if (!device)
212                 return -ENOMEM;
213
214         manager_process_link(m, device);
215         return 0;
216 }
217
218 int manager_udev_listen(Manager *m) {
219         int r;
220
221         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
222         if (r < 0) {
223                 log_error("Could not add udev monitor filter: %s", strerror(-r));
224                 return r;
225         }
226
227         r = udev_monitor_enable_receiving(m->udev_monitor);
228         if (r < 0) {
229                 log_error("Could not enable udev monitor");
230                 return r;
231         }
232
233         r = sd_event_add_io(m->event,
234                         udev_monitor_get_fd(m->udev_monitor),
235                         EPOLLIN, manager_dispatch_link_udev,
236                         m, &m->udev_event_source);
237         if (r < 0)
238                 return r;
239
240         return 0;
241 }
242
243 static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
244         Manager *m = userdata;
245         Link *link;
246         int r, ifindex;
247
248         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
249         if (r < 0)
250                 return 0;
251
252         link = hashmap_get(m->links, &ifindex);
253         if (!link)
254                 return 0;
255
256         r = link_update(link, message);
257         if (r < 0)
258                 return 0;
259
260         return 1;
261 }
262
263 int manager_rtnl_listen(Manager *m) {
264         int r;
265
266         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
267         if (r < 0)
268                 return r;
269
270         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
271         if (r < 0)
272                 return r;
273
274         return 0;
275 }