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