chiark / gitweb /
networkd-wait-online: drop config file and add commandline options instead
[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         STRV_FOREACH(ifname, arg_interfaces) {
120                 _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
121                 bool found = false;
122                 int index;
123
124                 r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, 0);
125                 if (r < 0) {
126                         log_warning("colud not create GETLINK message: %s", strerror(-r));
127                         return false;
128                 }
129
130                 r = sd_rtnl_message_append_string(message, IFLA_IFNAME, *ifname);
131                 if (r < 0) {
132                         log_warning("could not attach ifname to GETLINK message: %s", strerror(-r));
133                         return false;
134                 }
135
136                 r = sd_rtnl_call(m->rtnl, message, 0, &reply);
137                 if (r < 0) {
138                         if (r != -ENODEV)
139                                 log_warning("could not get link info for %s: %s", *ifname,
140                                             strerror(-r));
141
142                         /* link does not yet exist */
143                         return false;
144                 }
145
146                 r = sd_rtnl_message_link_get_ifindex(reply, &index);
147                 if (r < 0) {
148                         log_warning("could not get ifindex: %s", strerror(-r));
149                         return false;
150                 }
151
152                 if (index <= 0) {
153                         log_warning("invalid ifindex %d for %s", index, *ifname);
154                         return false;
155                 }
156
157                 for (i = 0; i < n; i++) {
158                         if (indices[i] == (unsigned) index) {
159                                 found = true;
160                                 break;
161                         }
162                 }
163
164                 if (!found) {
165                         /* link exists, but networkd is not yet aware of it */
166                         return false;
167         }
168
169         for (i = 0; i < n; i++) {
170                 _cleanup_free_ char *state = NULL;
171
172                 r = sd_network_get_link_state(indices[i], &state);
173                 if (r == -EUNATCH) {
174                         _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
175                         unsigned flags;
176                         uint8_t operstate;
177
178                         r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, indices[i]);
179                         if (r < 0) {
180                                 log_warning("could not create GETLINK message: %s", strerror(-r));
181                                 return false;
182                         }
183
184                         r = sd_rtnl_call(m->rtnl, message, 0, &reply);
185                         if (r < 0) {
186                                 log_debug("could not get link %u: %s", indices[i], strerror(-r));
187                                 continue;
188                         }
189
190                         r = sd_rtnl_message_link_get_flags(reply, &flags);
191                         if (r < 0) {
192                                 log_warning("could not get link flags: %s", strerror(-r));
193                                 return false;
194                         }
195
196                         r = sd_rtnl_message_read_u8(reply, IFLA_OPERSTATE, &operstate);
197                         if (r < 0) {
198                                 log_debug("could not get link operational state: %s", strerror(-r));
199                                 operstate = IF_OPER_UNKNOWN;
200                         }
201                         
202                         if (!(flags & IFF_LOOPBACK) &&
203                             link_has_carrier(flags, operstate)) {
204                                 /* this link is not managed by us,
205                                    but something else may have
206                                    made it ready, so don't block */
207                                 one_ready = true;
208                         }
209
210                         continue;
211                 } else if (r < 0 || !streq(state, "configured"))
212                         /* managed by networkd, but not yet configured */
213                         return false;
214
215                 /* we wait for at least one link to appear */
216                 one_ready = true;
217         }
218
219         return one_ready;
220 }
221
222 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
223                          void *userdata) {
224         Manager *m = userdata;
225
226         assert(m);
227         assert(m->event);
228
229         if (all_configured(m))
230                 sd_event_exit(m->event, 0);
231
232         return 1;
233 }
234
235 static int newlink_event_handler(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
236         Manager *m = userdata;
237
238         assert(m);
239         assert(m->event);
240
241         if (all_configured(m))
242                 sd_event_exit(m->event, 0);
243
244         return 1;
245 }
246
247 void manager_free(Manager *m) {
248         if (!m)
249                 return;
250
251         sd_event_unref(m->event);
252         sd_rtnl_unref(m->rtnl);
253
254         free(m);
255 }
256
257 int main(int argc, char *argv[]) {
258         _cleanup_manager_free_ Manager *m = NULL;
259         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
260         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
261         int r, fd, events;
262
263         umask(0022);
264
265         log_parse_environment();
266         log_open();
267
268         r = parse_argv(argc, argv);
269         if (r <= 0)
270                 return r;
271
272         if (arg_quiet)
273                 log_set_max_level(LOG_WARNING);
274
275         m = new0(Manager, 1);
276         if (!m)
277                 return log_oom();
278
279         r = sd_network_monitor_new(NULL, &monitor);
280         if (r < 0) {
281                 log_error("Could not create monitor: %s", strerror(-r));
282                 goto out;
283         }
284
285         r = sd_event_new(&m->event);
286         if (r < 0) {
287                 log_error("Could not create event: %s", strerror(-r));
288                 goto out;
289         }
290
291         fd = sd_network_monitor_get_fd(monitor);
292         if (fd < 0) {
293                 log_error("Could not get monitor fd: %s", strerror(-r));
294                 goto out;
295         }
296
297         events = sd_network_monitor_get_events(monitor);
298         if (events < 0) {
299                 log_error("Could not get monitor events: %s", strerror(-r));
300                 goto out;
301         }
302
303         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
304                             m);
305         if (r < 0) {
306                 log_error("Could not add io event source: %s", strerror(-r));
307                 goto out;
308         }
309
310         r = sd_rtnl_open(&m->rtnl, RTMGRP_LINK);
311         if (r < 0) {
312                 log_error("Could not create rtnl: %s", strerror(-r));
313                 goto out;
314         }
315
316         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
317         if (r < 0) {
318                 log_error("Could not attach event to rtnl: %s", strerror(-r));
319                 return r;
320         }
321
322         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, newlink_event_handler, m);
323         if (r < 0)
324                 return r;
325
326         if (all_configured(m)) {
327                 r = 0;
328                 goto out;
329         }
330
331         sd_notify(false,
332                   "READY=1\n"
333                   "STATUS=Waiting for network connections...");
334
335         r = sd_event_loop(m->event);
336         if (r < 0) {
337                 log_error("Event loop failed: %s", strerror(-r));
338                 goto out;
339         }
340
341 out:
342         sd_notify(false,
343                   "STATUS=All interfaces configured...");
344
345         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
346 }