chiark / gitweb /
networkd-wait-online: fix false positives when checking if a link is managed by networkd
[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 #include <getopt.h>
25
26 #include "sd-event.h"
27 #include "event-util.h"
28 #include "sd-rtnl.h"
29 #include "rtnl-util.h"
30 #include "sd-daemon.h"
31 #include "sd-network.h"
32 #include "network-util.h"
33 #include "network-internal.h"
34 #include "networkd-wait-online.h"
35
36 #include "conf-parser.h"
37 #include "strv.h"
38 #include "util.h"
39 #include "build.h"
40
41 static bool arg_quiet = false;
42 static char **arg_interfaces = NULL;
43
44 static int help(void) {
45
46         printf("%s [OPTIONS...]\n\n"
47                "Block until network is configured.\n\n"
48                "  -h --help                 Show this help\n"
49                "     --version              Print version string\n"
50                "  -q --quiet                Do not show status information\n"
51                "  -i --interface=INTERFACE  Block until at least these interfaces have appeared\n",
52                program_invocation_short_name);
53
54         return 0;
55 }
56
57 static int parse_argv(int argc, char *argv[]) {
58
59         enum {
60                 ARG_VERSION = 0x100,
61         };
62
63         static const struct option options[] = {
64                 { "help",            no_argument,       NULL, 'h'         },
65                 { "version",         no_argument,       NULL, ARG_VERSION },
66                 { "quiet",           no_argument,       NULL, 'q'         },
67                 { "interface",       required_argument, NULL, 'i'         },
68                 {}
69         };
70
71         int c;
72
73         assert(argc >= 0);
74         assert(argv);
75
76         while ((c = getopt_long(argc, argv, "+hq", options, NULL)) >= 0) {
77
78                 switch (c) {
79
80                 case 'h':
81                         return help();
82
83                 case 'q':
84                         arg_quiet = true;
85                         break;
86
87                 case ARG_VERSION:
88                         puts(PACKAGE_STRING);
89                         puts(SYSTEMD_FEATURES);
90                         return 0;
91
92                 case 'i':
93                         if (strv_extend(&arg_interfaces, optarg) < 0)
94                                 return log_oom();
95
96                         break;
97
98                 case '?':
99                         return -EINVAL;
100
101                 default:
102                         assert_not_reached("Unhandled option");
103                 }
104         }
105
106         return 1;
107 }
108
109 static bool all_configured(Manager *m) {
110         _cleanup_free_ unsigned *indices = NULL;
111         char **ifname;
112         bool one_ready = false;
113         int r, n, i;
114
115         n = sd_network_get_ifindices(&indices);
116         if (n <= 0)
117                 return false;
118
119         /* wait for networkd to be aware of all the links given on the commandline */
120         STRV_FOREACH(ifname, arg_interfaces) {
121                 _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
122                 bool found = false;
123                 int index;
124
125                 r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, 0);
126                 if (r < 0) {
127                         log_warning("colud not create GETLINK message: %s", strerror(-r));
128                         return false;
129                 }
130
131                 r = sd_rtnl_message_append_string(message, IFLA_IFNAME, *ifname);
132                 if (r < 0) {
133                         log_warning("could not attach ifname to GETLINK message: %s", strerror(-r));
134                         return false;
135                 }
136
137                 r = sd_rtnl_call(m->rtnl, message, 0, &reply);
138                 if (r < 0) {
139                         if (r != -ENODEV)
140                                 log_warning("could not get link info for %s: %s", *ifname,
141                                             strerror(-r));
142
143                         /* link does not yet exist */
144                         return false;
145                 }
146
147                 r = sd_rtnl_message_link_get_ifindex(reply, &index);
148                 if (r < 0) {
149                         log_warning("could not get ifindex: %s", strerror(-r));
150                         return false;
151                 }
152
153                 if (index <= 0) {
154                         log_warning("invalid ifindex %d for %s", index, *ifname);
155                         return false;
156                 }
157
158                 for (i = 0; i < n; i++) {
159                         if (indices[i] == (unsigned) index) {
160                                 found = true;
161                                 break;
162                         }
163                 }
164
165                 if (!found) {
166                         /* link exists, but networkd is not yet aware of it */
167                         return false;
168                 }
169         }
170
171         /* wait for all links networkd manages to be in admin state 'configured'
172            and at least one link to gain a carrier */
173         for (i = 0; i < n; i++) {
174                 _cleanup_free_ char *state = NULL, *oper_state = NULL;
175
176                 if (sd_network_link_is_loopback(indices[i]))
177                         /* ignore loopback devices */
178                         continue;
179
180                 r = sd_network_get_link_state(indices[i], &state);
181                 if (r == -EBUSY || (r >= 0 && !streq(state, "configured")))
182                         /* not yet processed by udev, or managed by networkd, but not yet configured */
183                         return false;
184
185                 r = sd_network_get_link_operational_state(indices[i], &oper_state);
186                 if (r >= 0 && streq(oper_state, "carrier"))
187                         /* we wait for at least one link to be ready,
188                            regardless of who manages it */
189                         one_ready = true;
190         }
191
192         return one_ready;
193 }
194
195 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
196                          void *userdata) {
197         Manager *m = userdata;
198
199         assert(m);
200         assert(m->event);
201
202         if (all_configured(m))
203                 sd_event_exit(m->event, 0);
204
205         return 1;
206 }
207
208 void manager_free(Manager *m) {
209         if (!m)
210                 return;
211
212         sd_event_unref(m->event);
213         sd_rtnl_unref(m->rtnl);
214
215         free(m);
216 }
217
218 int main(int argc, char *argv[]) {
219         _cleanup_manager_free_ Manager *m = NULL;
220         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
221         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
222         int r, fd, events;
223
224         umask(0022);
225
226         log_parse_environment();
227         log_open();
228
229         r = parse_argv(argc, argv);
230         if (r <= 0)
231                 return r;
232
233         if (arg_quiet)
234                 log_set_max_level(LOG_WARNING);
235
236         m = new0(Manager, 1);
237         if (!m)
238                 return log_oom();
239
240         r = sd_network_monitor_new(NULL, &monitor);
241         if (r < 0) {
242                 log_error("Could not create monitor: %s", strerror(-r));
243                 goto out;
244         }
245
246         r = sd_event_new(&m->event);
247         if (r < 0) {
248                 log_error("Could not create event: %s", strerror(-r));
249                 goto out;
250         }
251
252         fd = sd_network_monitor_get_fd(monitor);
253         if (fd < 0) {
254                 log_error("Could not get monitor fd: %s", strerror(-r));
255                 goto out;
256         }
257
258         events = sd_network_monitor_get_events(monitor);
259         if (events < 0) {
260                 log_error("Could not get monitor events: %s", strerror(-r));
261                 goto out;
262         }
263
264         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
265                             m);
266         if (r < 0) {
267                 log_error("Could not add io event source: %s", strerror(-r));
268                 goto out;
269         }
270
271         r = sd_rtnl_open(&m->rtnl, 0);
272         if (r < 0) {
273                 log_error("Could not create rtnl: %s", strerror(-r));
274                 goto out;
275         }
276
277         if (all_configured(m)) {
278                 r = 0;
279                 goto out;
280         }
281
282         sd_notify(false,
283                   "READY=1\n"
284                   "STATUS=Waiting for network connections...");
285
286         r = sd_event_loop(m->event);
287         if (r < 0) {
288                 log_error("Event loop failed: %s", strerror(-r));
289                 goto out;
290         }
291
292 out:
293         sd_notify(false,
294                   "STATUS=All interfaces configured...");
295
296         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
297 }