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