chiark / gitweb /
networkd-wait-online: improve interoptability and enable by default
[elogind.git] / src / network / networkd-wait-online.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
25 #include "sd-event.h"
26 #include "event-util.h"
27 #include "sd-rtnl.h"
28 #include "rtnl-util.h"
29 #include "sd-daemon.h"
30 #include "sd-network.h"
31 #include "network-util.h"
32 #include "network-internal.h"
33 #include "networkd-wait-online.h"
34
35 #include "conf-parser.h"
36 #include "strv.h"
37 #include "util.h"
38
39 static bool all_configured(Manager *m) {
40         _cleanup_free_ unsigned *indices = NULL;
41         char **ifname;
42         bool one_ready = false;
43         int r, n, i;
44
45         n = sd_network_get_ifindices(&indices);
46         if (n <= 0)
47                 return false;
48
49         STRV_FOREACH(ifname, m->expected_links) {
50                 _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
51                 bool found = false;
52                 int index;
53
54                 r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, 0);
55                 if (r < 0) {
56                         log_warning("colud not create GETLINK message: %s", strerror(-r));
57                         return false;
58                 }
59
60                 r = sd_rtnl_message_append_string(message, IFLA_IFNAME, *ifname);
61                 if (r < 0) {
62                         log_warning("could not attach ifname to GETLINK message: %s", strerror(-r));
63                         return false;
64                 }
65
66                 r = sd_rtnl_call(m->rtnl, message, 0, &reply);
67                 if (r < 0) {
68                         if (r != -ENODEV)
69                                 log_warning("could not get link info for %s: %s", *ifname,
70                                             strerror(-r));
71
72                         /* link does not yet exist */
73                         return false;
74                 }
75
76                 r = sd_rtnl_message_link_get_ifindex(reply, &index);
77                 if (r < 0) {
78                         log_warning("could not get ifindex: %s", strerror(-r));
79                         return false;
80                 }
81
82                 if (index <= 0) {
83                         log_warning("invalid ifindex %d for %s", index, *ifname);
84                         return false;
85                 }
86
87                 for (i = 0; i < n; i++) {
88                         if (indices[i] == (unsigned) index) {
89                                 found = true;
90                                 break;
91                         }
92                 }
93
94                 if (!found)
95                         /* link exists, but networkd is not yet aware of it */
96                         return false;
97         }
98
99         for (i = 0; i < n; i++) {
100                 _cleanup_free_ char *state = NULL;
101
102                 r = sd_network_get_link_state(indices[i], &state);
103                 if (r == -EUNATCH) {
104                         _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
105                         unsigned flags;
106                         uint8_t operstate;
107
108                         r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, indices[i]);
109                         if (r < 0) {
110                                 log_warning("could not create GETLINK message: %s", strerror(-r));
111                                 return false;
112                         }
113
114                         r = sd_rtnl_call(m->rtnl, message, 0, &reply);
115                         if (r < 0) {
116                                 log_debug("could not get link %u: %s", indices[i], strerror(-r));
117                                 continue;
118                         }
119
120                         r = sd_rtnl_message_link_get_flags(reply, &flags);
121                         if (r < 0) {
122                                 log_warning("could not get link flags: %s", strerror(-r));
123                                 return false;
124                         }
125
126                         r = sd_rtnl_message_read_u8(reply, IFLA_OPERSTATE, &operstate);
127                         if (r < 0) {
128                                 log_debug("could not get link operational state: %s", strerror(-r));
129                                 operstate = IF_OPER_UNKNOWN;
130                         }
131                         
132                         if (!(flags & IFF_LOOPBACK) &&
133                             link_has_carrier(flags, operstate)) {
134                                 /* this link is not managed by us,
135                                    but something else may have
136                                    made it ready, so don't block */
137                                 one_ready = true;
138                         }
139
140                         continue;
141                 } else if (r < 0 || !streq(state, "configured"))
142                         return false;
143
144                 /* we wait for at least one link to appear */
145                 one_ready = true;
146         }
147
148         return one_ready;
149 }
150
151 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
152                          void *userdata) {
153         Manager *m = userdata;
154
155         assert(m);
156         assert(m->event);
157
158         if (all_configured(m))
159                 sd_event_exit(m->event, 0);
160
161         return 1;
162 }
163
164 static int newlink_event_handler(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
165         Manager *m = userdata;
166
167         assert(m);
168         assert(m->event);
169
170         if (all_configured(m))
171                 sd_event_exit(m->event, 0);
172
173         return 1;
174 }
175
176 static int parse_config_file(Manager *m) {
177         static const char fn[] = "/etc/systemd/networkd-wait-online.conf";
178         _cleanup_fclose_ FILE *f = NULL;
179         int r;
180
181         f = fopen(fn, "re");
182         if (!f) {
183                 if (errno == ENOENT)
184                         return 0;
185
186                 log_warning("Failed to open configuration file %s: %m", fn);
187                 return -errno;
188         }
189
190         r = config_parse(NULL, fn, f, "WaitOnline\0", config_item_perf_lookup,
191                          (void*) wait_online_gperf_lookup, false, false, m);
192         if (r < 0)
193                 log_warning("Failed to parse configuration file: %s", strerror(-r));
194
195         return r;
196 }
197
198 void manager_free(Manager *m) {
199         if (!m)
200                 return;
201
202         sd_event_unref(m->event);
203         sd_rtnl_unref(m->rtnl);
204         strv_free(m->expected_links);
205
206         free(m);
207 }
208
209 int main(int argc, char *argv[]) {
210         _cleanup_manager_free_ Manager *m = NULL;
211         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
212         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
213         int r, fd, events;
214
215         log_set_target(LOG_TARGET_AUTO);
216         log_parse_environment();
217         log_open();
218
219         umask(0022);
220
221         if (argc != 1) {
222                 log_error("This program takes no arguments.");
223                 r = -EINVAL;
224                 goto out;
225         }
226
227         m = new0(Manager, 1);
228         if (!m)
229                 return log_oom();
230
231         r = parse_config_file(m);
232         if (r < 0)
233                 goto out;
234
235         r = sd_network_monitor_new(NULL, &monitor);
236         if (r < 0) {
237                 log_error("Could not create monitor: %s", strerror(-r));
238                 goto out;
239         }
240
241         r = sd_event_new(&m->event);
242         if (r < 0) {
243                 log_error("Could not create event: %s", strerror(-r));
244                 goto out;
245         }
246
247         fd = sd_network_monitor_get_fd(monitor);
248         if (fd < 0) {
249                 log_error("Could not get monitor fd: %s", strerror(-r));
250                 goto out;
251         }
252
253         events = sd_network_monitor_get_events(monitor);
254         if (events < 0) {
255                 log_error("Could not get monitor events: %s", strerror(-r));
256                 goto out;
257         }
258
259         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
260                             m);
261         if (r < 0) {
262                 log_error("Could not add io event source: %s", strerror(-r));
263                 goto out;
264         }
265
266         r = sd_rtnl_open(&m->rtnl, RTMGRP_LINK);
267         if (r < 0) {
268                 log_error("Could not create rtnl: %s", strerror(-r));
269                 goto out;
270         }
271
272         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
273         if (r < 0) {
274                 log_error("Could not attach event to rtnl: %s", strerror(-r));
275                 return r;
276         }
277
278         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, newlink_event_handler, m);
279         if (r < 0)
280                 return r;
281
282         if (all_configured(m)) {
283                 r = 0;
284                 goto out;
285         }
286
287         sd_notify(false,
288                   "READY=1\n"
289                   "STATUS=Waiting for network connections...");
290
291         r = sd_event_loop(m->event);
292         if (r < 0) {
293                 log_error("Event loop failed: %s", strerror(-r));
294                 goto out;
295         }
296
297 out:
298         sd_notify(false,
299                   "STATUS=All interfaces configured...");
300
301         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
302 }