]>
Commit | Line | Data |
---|---|---|
020d5900 TG |
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 | ||
3a67e927 TG |
22 | #include <netinet/ether.h> |
23 | #include <linux/if.h> | |
cef8b073 | 24 | #include <getopt.h> |
3a67e927 | 25 | |
020d5900 | 26 | #include "sd-event.h" |
e1528e08 | 27 | #include "event-util.h" |
3a67e927 TG |
28 | #include "sd-rtnl.h" |
29 | #include "rtnl-util.h" | |
020d5900 TG |
30 | #include "sd-daemon.h" |
31 | #include "sd-network.h" | |
e1528e08 | 32 | #include "network-util.h" |
3a67e927 TG |
33 | #include "network-internal.h" |
34 | #include "networkd-wait-online.h" | |
020d5900 | 35 | |
3a67e927 TG |
36 | #include "conf-parser.h" |
37 | #include "strv.h" | |
020d5900 | 38 | #include "util.h" |
cef8b073 TG |
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 | } | |
020d5900 | 108 | |
3a67e927 | 109 | static bool all_configured(Manager *m) { |
020d5900 | 110 | _cleanup_free_ unsigned *indices = NULL; |
3a67e927 | 111 | char **ifname; |
020d5900 TG |
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 | ||
bc634576 | 119 | /* wait for networkd to be aware of all the links given on the commandline */ |
cef8b073 | 120 | STRV_FOREACH(ifname, arg_interfaces) { |
3a67e927 TG |
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) { | |
865cc19a | 127 | log_warning("could not create GETLINK message: %s", strerror(-r)); |
3a67e927 TG |
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 | ||
cef8b073 | 165 | if (!found) { |
3a67e927 TG |
166 | /* link exists, but networkd is not yet aware of it */ |
167 | return false; | |
27134b2f | 168 | } |
3a67e927 TG |
169 | } |
170 | ||
bc634576 TG |
171 | /* wait for all links networkd manages to be in admin state 'configured' |
172 | and at least one link to gain a carrier */ | |
020d5900 | 173 | for (i = 0; i < n; i++) { |
bc634576 TG |
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; | |
020d5900 TG |
179 | |
180 | r = sd_network_get_link_state(indices[i], &state); | |
d91d3c15 TG |
181 | if (r == -EBUSY || (r >= 0 && !streq(state, "configured"))) |
182 | /* not yet processed by udev, or managed by networkd, but not yet configured */ | |
27134b2f | 183 | return false; |
3a67e927 | 184 | |
bc634576 | 185 | r = sd_network_get_link_operational_state(indices[i], &oper_state); |
473dfd7b TG |
186 | if (r >= 0 && |
187 | (streq(oper_state, "degraded") || | |
188 | streq(oper_state, "routable"))) | |
27134b2f TG |
189 | /* we wait for at least one link to be ready, |
190 | regardless of who manages it */ | |
191 | one_ready = true; | |
020d5900 TG |
192 | } |
193 | ||
cb6fa44c | 194 | return one_ready; |
020d5900 TG |
195 | } |
196 | ||
3a67e927 | 197 | static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents, |
020d5900 | 198 | void *userdata) { |
3a67e927 TG |
199 | Manager *m = userdata; |
200 | ||
201 | assert(m); | |
202 | assert(m->event); | |
203 | ||
204 | if (all_configured(m)) | |
205 | sd_event_exit(m->event, 0); | |
206 | ||
3a9c5a32 TG |
207 | sd_network_monitor_flush(m->monitor); |
208 | ||
3a67e927 TG |
209 | return 1; |
210 | } | |
020d5900 | 211 | |
3a67e927 TG |
212 | void manager_free(Manager *m) { |
213 | if (!m) | |
214 | return; | |
215 | ||
216 | sd_event_unref(m->event); | |
217 | sd_rtnl_unref(m->rtnl); | |
3a67e927 TG |
218 | |
219 | free(m); | |
220 | } | |
221 | ||
020d5900 | 222 | int main(int argc, char *argv[]) { |
3a67e927 | 223 | _cleanup_manager_free_ Manager *m = NULL; |
e1528e08 | 224 | _cleanup_event_source_unref_ sd_event_source *event_source = NULL; |
020d5900 TG |
225 | int r, fd, events; |
226 | ||
cef8b073 TG |
227 | umask(0022); |
228 | ||
020d5900 TG |
229 | log_parse_environment(); |
230 | log_open(); | |
231 | ||
cef8b073 TG |
232 | r = parse_argv(argc, argv); |
233 | if (r <= 0) | |
234 | return r; | |
020d5900 | 235 | |
cef8b073 TG |
236 | if (arg_quiet) |
237 | log_set_max_level(LOG_WARNING); | |
020d5900 | 238 | |
3a67e927 TG |
239 | m = new0(Manager, 1); |
240 | if (!m) | |
241 | return log_oom(); | |
242 | ||
3a9c5a32 | 243 | r = sd_event_new(&m->event); |
020d5900 | 244 | if (r < 0) { |
3a9c5a32 | 245 | log_error("Could not create event: %s", strerror(-r)); |
020d5900 TG |
246 | goto out; |
247 | } | |
248 | ||
3a9c5a32 | 249 | r = sd_rtnl_open(&m->rtnl, 0); |
020d5900 | 250 | if (r < 0) { |
3a9c5a32 | 251 | log_error("Could not create rtnl: %s", strerror(-r)); |
020d5900 TG |
252 | goto out; |
253 | } | |
254 | ||
0014a4ad | 255 | r = sd_network_monitor_new(&m->monitor, NULL); |
3a9c5a32 TG |
256 | if (r < 0) { |
257 | log_error("Could not create monitor: %s", strerror(-r)); | |
258 | goto out; | |
259 | } | |
260 | ||
261 | fd = sd_network_monitor_get_fd(m->monitor); | |
020d5900 TG |
262 | if (fd < 0) { |
263 | log_error("Could not get monitor fd: %s", strerror(-r)); | |
264 | goto out; | |
265 | } | |
266 | ||
3a9c5a32 | 267 | events = sd_network_monitor_get_events(m->monitor); |
020d5900 TG |
268 | if (events < 0) { |
269 | log_error("Could not get monitor events: %s", strerror(-r)); | |
270 | goto out; | |
271 | } | |
272 | ||
3a67e927 TG |
273 | r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler, |
274 | m); | |
020d5900 TG |
275 | if (r < 0) { |
276 | log_error("Could not add io event source: %s", strerror(-r)); | |
277 | goto out; | |
278 | } | |
279 | ||
3a67e927 | 280 | if (all_configured(m)) { |
020d5900 TG |
281 | r = 0; |
282 | goto out; | |
283 | } | |
284 | ||
285 | sd_notify(false, | |
286 | "READY=1\n" | |
b6b8adbf | 287 | "STATUS=Waiting for network connections..."); |
020d5900 | 288 | |
3a67e927 | 289 | r = sd_event_loop(m->event); |
020d5900 TG |
290 | if (r < 0) { |
291 | log_error("Event loop failed: %s", strerror(-r)); | |
292 | goto out; | |
293 | } | |
294 | ||
295 | out: | |
296 | sd_notify(false, | |
297 | "STATUS=All interfaces configured..."); | |
298 | ||
020d5900 TG |
299 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
300 | } |