chiark / gitweb /
networkd-wait-online: flush monitor events after processing
[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         sd_network_monitor_flush(m->monitor);
206
207         return 1;
208 }
209
210 void manager_free(Manager *m) {
211         if (!m)
212                 return;
213
214         sd_event_unref(m->event);
215         sd_rtnl_unref(m->rtnl);
216
217         free(m);
218 }
219
220 int main(int argc, char *argv[]) {
221         _cleanup_manager_free_ Manager *m = NULL;
222         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
223         int r, fd, events;
224
225         umask(0022);
226
227         log_parse_environment();
228         log_open();
229
230         r = parse_argv(argc, argv);
231         if (r <= 0)
232                 return r;
233
234         if (arg_quiet)
235                 log_set_max_level(LOG_WARNING);
236
237         m = new0(Manager, 1);
238         if (!m)
239                 return log_oom();
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         r = sd_rtnl_open(&m->rtnl, 0);
248         if (r < 0) {
249                 log_error("Could not create rtnl: %s", strerror(-r));
250                 goto out;
251         }
252
253         r = sd_network_monitor_new(NULL, &m->monitor);
254         if (r < 0) {
255                 log_error("Could not create monitor: %s", strerror(-r));
256                 goto out;
257         }
258
259         fd = sd_network_monitor_get_fd(m->monitor);
260         if (fd < 0) {
261                 log_error("Could not get monitor fd: %s", strerror(-r));
262                 goto out;
263         }
264
265         events = sd_network_monitor_get_events(m->monitor);
266         if (events < 0) {
267                 log_error("Could not get monitor events: %s", strerror(-r));
268                 goto out;
269         }
270
271         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
272                             m);
273         if (r < 0) {
274                 log_error("Could not add io event source: %s", strerror(-r));
275                 goto out;
276         }
277
278         if (all_configured(m)) {
279                 r = 0;
280                 goto out;
281         }
282
283         sd_notify(false,
284                   "READY=1\n"
285                   "STATUS=Waiting for network connections...");
286
287         r = sd_event_loop(m->event);
288         if (r < 0) {
289                 log_error("Event loop failed: %s", strerror(-r));
290                 goto out;
291         }
292
293 out:
294         sd_notify(false,
295                   "STATUS=All interfaces configured...");
296
297         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
298 }