]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-wait-online-manager.c
112d92a568a74f4b2449dceec4bb61ac35068870
[thirdparty/systemd.git] / src / network / networkd-wait-online-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 <netinet/ether.h>
23 #include <linux/if.h>
24 #include <fnmatch.h>
25
26 #include "netlink-util.h"
27
28 #include "network-internal.h"
29 #include "networkd-wait-online-link.h"
30 #include "networkd-wait-online.h"
31
32 #include "util.h"
33 #include "time-util.h"
34
35 bool manager_ignore_link(Manager *m, Link *link) {
36 char **ignore;
37
38 assert(m);
39 assert(link);
40
41 /* always ignore the loopback interface */
42 if (link->flags & IFF_LOOPBACK)
43 return true;
44
45 /* if interfaces are given on the command line, ignore all others */
46 if (m->interfaces && !strv_contains(m->interfaces, link->ifname))
47 return true;
48
49 /* ignore interfaces we explicitly are asked to ignore */
50 STRV_FOREACH(ignore, m->ignore)
51 if (fnmatch(*ignore, link->ifname, 0) == 0)
52 return true;
53
54 return false;
55 }
56
57 bool manager_all_configured(Manager *m) {
58 Iterator i;
59 Link *l;
60 char **ifname;
61 bool one_ready = false;
62
63 /* wait for all the links given on the command line to appear */
64 STRV_FOREACH(ifname, m->interfaces) {
65 l = hashmap_get(m->links_by_name, *ifname);
66 if (!l) {
67 log_debug("still waiting for %s", *ifname);
68 return false;
69 }
70 }
71
72 /* wait for all links networkd manages to be in admin state 'configured'
73 and at least one link to gain a carrier */
74 HASHMAP_FOREACH(l, m->links, i) {
75 if (manager_ignore_link(m, l)) {
76 log_info("ignoring: %s", l->ifname);
77 continue;
78 }
79
80 if (!l->state) {
81 log_debug("link %s has not yet been processed by udev",
82 l->ifname);
83 return false;
84 }
85
86 if (STR_IN_SET(l->state, "configuring", "pending")) {
87 log_debug("link %s is being processed by networkd",
88 l->ifname);
89 return false;
90 }
91
92 if (l->operational_state &&
93 STR_IN_SET(l->operational_state, "degraded", "routable"))
94 /* we wait for at least one link to be ready,
95 regardless of who manages it */
96 one_ready = true;
97 }
98
99 return one_ready;
100 }
101
102 static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
103 Manager *m = userdata;
104 uint16_t type;
105 Link *l;
106 const char *ifname;
107 int ifindex, r;
108
109 assert(rtnl);
110 assert(m);
111 assert(mm);
112
113 r = sd_netlink_message_get_type(mm, &type);
114 if (r < 0)
115 goto fail;
116
117 r = sd_rtnl_message_link_get_ifindex(mm, &ifindex);
118 if (r < 0)
119 goto fail;
120
121 r = sd_netlink_message_read_string(mm, IFLA_IFNAME, &ifname);
122 if (r < 0)
123 goto fail;
124
125 l = hashmap_get(m->links, INT_TO_PTR(ifindex));
126
127 switch (type) {
128
129 case RTM_NEWLINK:
130 if (!l) {
131 log_debug("Found link %i", ifindex);
132
133 r = link_new(m, &l, ifindex, ifname);
134 if (r < 0)
135 goto fail;
136
137 r = link_update_monitor(l);
138 if (r < 0)
139 goto fail;
140 }
141
142 r = link_update_rtnl(l, mm);
143 if (r < 0)
144 goto fail;
145
146 break;
147
148 case RTM_DELLINK:
149 if (l) {
150 log_debug("Removing link %i", l->ifindex);
151 link_free(l);
152 }
153
154 break;
155 }
156
157 return 0;
158
159 fail:
160 log_warning_errno(r, "Failed to process RTNL link message: %m");
161 return 0;
162 }
163
164 static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
165 Manager *m = userdata;
166 int r;
167
168 r = manager_process_link(rtnl, mm, m);
169 if (r < 0)
170 return r;
171
172 if (manager_all_configured(m))
173 sd_event_exit(m->event, 0);
174
175 return 1;
176 }
177
178 static int manager_rtnl_listen(Manager *m) {
179 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
180 sd_netlink_message *i;
181 int r;
182
183 assert(m);
184
185 /* First, subscribe to interfaces coming and going */
186 r = sd_netlink_open(&m->rtnl);
187 if (r < 0)
188 return r;
189
190 r = sd_netlink_attach_event(m->rtnl, m->event, 0);
191 if (r < 0)
192 return r;
193
194 r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, on_rtnl_event, m);
195 if (r < 0)
196 return r;
197
198 r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, on_rtnl_event, m);
199 if (r < 0)
200 return r;
201
202 /* Then, enumerate all links */
203 r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
204 if (r < 0)
205 return r;
206
207 r = sd_netlink_message_request_dump(req, true);
208 if (r < 0)
209 return r;
210
211 r = sd_netlink_call(m->rtnl, req, 0, &reply);
212 if (r < 0)
213 return r;
214
215 for (i = reply; i; i = sd_netlink_message_next(i)) {
216 r = manager_process_link(m->rtnl, i, m);
217 if (r < 0)
218 return r;
219 }
220
221 return r;
222 }
223
224 static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
225 Manager *m = userdata;
226 Iterator i;
227 Link *l;
228 int r;
229
230 assert(m);
231
232 sd_network_monitor_flush(m->network_monitor);
233
234 HASHMAP_FOREACH(l, m->links, i) {
235 r = link_update_monitor(l);
236 if (r < 0)
237 log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex);
238 }
239
240 if (manager_all_configured(m))
241 sd_event_exit(m->event, 0);
242
243 return 0;
244 }
245
246 static int manager_network_monitor_listen(Manager *m) {
247 int r, fd, events;
248
249 assert(m);
250
251 r = sd_network_monitor_new(&m->network_monitor, NULL);
252 if (r < 0)
253 return r;
254
255 fd = sd_network_monitor_get_fd(m->network_monitor);
256 if (fd < 0)
257 return fd;
258
259 events = sd_network_monitor_get_events(m->network_monitor);
260 if (events < 0)
261 return events;
262
263 r = sd_event_add_io(m->event, &m->network_monitor_event_source,
264 fd, events, &on_network_event, m);
265 if (r < 0)
266 return r;
267
268 return 0;
269 }
270
271 int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout) {
272 _cleanup_(manager_freep) Manager *m = NULL;
273 int r;
274
275 assert(ret);
276
277 m = new0(Manager, 1);
278 if (!m)
279 return -ENOMEM;
280
281 m->interfaces = interfaces;
282 m->ignore = ignore;
283
284 r = sd_event_default(&m->event);
285 if (r < 0)
286 return r;
287
288 sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
289 sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
290
291 if (timeout > 0) {
292 usec_t usec;
293
294 usec = now(clock_boottime_or_monotonic()) + timeout;
295
296 r = sd_event_add_time(m->event, NULL, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT));
297 if (r < 0)
298 return r;
299 }
300
301 sd_event_set_watchdog(m->event, true);
302
303 r = manager_network_monitor_listen(m);
304 if (r < 0)
305 return r;
306
307 r = manager_rtnl_listen(m);
308 if (r < 0)
309 return r;
310
311 *ret = m;
312 m = NULL;
313
314 return 0;
315 }
316
317 void manager_free(Manager *m) {
318 Link *l;
319
320 if (!m)
321 return;
322
323 while ((l = hashmap_first(m->links)))
324 link_free(l);
325 hashmap_free(m->links);
326 hashmap_free(m->links_by_name);
327
328 sd_event_source_unref(m->network_monitor_event_source);
329 sd_network_monitor_unref(m->network_monitor);
330
331 sd_event_source_unref(m->rtnl_event_source);
332 sd_netlink_unref(m->rtnl);
333
334 sd_event_unref(m->event);
335 free(m);
336
337 return;
338 }