chiark / gitweb /
networkd-wait-online: refactor a bit
[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
170         for (i = 0; i < n; i++) {
171                 _cleanup_free_ char *state = NULL;
172                 _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL, *reply = NULL;
173                 unsigned flags;
174                 uint8_t operstate;
175
176                 r = sd_network_get_link_state(indices[i], &state);
177                 if (r != -EUNATCH && (r < 0 || !streq(state, "configured"))) {
178                         /* managed by networkd, but not yet configured */
179                         return false;
180                 }
181
182                 r = sd_rtnl_message_new_link(m->rtnl, &message, RTM_GETLINK, indices[i]);
183                 if (r < 0) {
184                         log_warning("could not create GETLINK message: %s", strerror(-r));
185                         return false;
186                 }
187
188                 r = sd_rtnl_call(m->rtnl, message, 0, &reply);
189                 if (r < 0) {
190                         log_debug("could not get link %u: %s", indices[i], strerror(-r));
191                         continue;
192                 }
193
194                 r = sd_rtnl_message_link_get_flags(reply, &flags);
195                 if (r < 0) {
196                         log_warning("could not get link flags: %s", strerror(-r));
197                         return false;
198                 }
199
200                 r = sd_rtnl_message_read_u8(reply, IFLA_OPERSTATE, &operstate);
201                 if (r < 0) {
202                         log_debug("could not get link operational state: %s", strerror(-r));
203                         operstate = IF_OPER_UNKNOWN;
204                 }
205
206                 if (!(flags & IFF_LOOPBACK) &&
207                     link_has_carrier(flags, operstate)) {
208                         /* we wait for at least one link to be ready,
209                            regardless of who manages it */
210                         one_ready = true;
211                 }
212         }
213
214         return one_ready;
215 }
216
217 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
218                          void *userdata) {
219         Manager *m = userdata;
220
221         assert(m);
222         assert(m->event);
223
224         if (all_configured(m))
225                 sd_event_exit(m->event, 0);
226
227         return 1;
228 }
229
230 static int newlink_event_handler(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
231         Manager *m = userdata;
232
233         assert(m);
234         assert(m->event);
235
236         if (all_configured(m))
237                 sd_event_exit(m->event, 0);
238
239         return 1;
240 }
241
242 void manager_free(Manager *m) {
243         if (!m)
244                 return;
245
246         sd_event_unref(m->event);
247         sd_rtnl_unref(m->rtnl);
248
249         free(m);
250 }
251
252 int main(int argc, char *argv[]) {
253         _cleanup_manager_free_ Manager *m = NULL;
254         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
255         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL;
256         int r, fd, events;
257
258         umask(0022);
259
260         log_parse_environment();
261         log_open();
262
263         r = parse_argv(argc, argv);
264         if (r <= 0)
265                 return r;
266
267         if (arg_quiet)
268                 log_set_max_level(LOG_WARNING);
269
270         m = new0(Manager, 1);
271         if (!m)
272                 return log_oom();
273
274         r = sd_network_monitor_new(NULL, &monitor);
275         if (r < 0) {
276                 log_error("Could not create monitor: %s", strerror(-r));
277                 goto out;
278         }
279
280         r = sd_event_new(&m->event);
281         if (r < 0) {
282                 log_error("Could not create event: %s", strerror(-r));
283                 goto out;
284         }
285
286         fd = sd_network_monitor_get_fd(monitor);
287         if (fd < 0) {
288                 log_error("Could not get monitor fd: %s", strerror(-r));
289                 goto out;
290         }
291
292         events = sd_network_monitor_get_events(monitor);
293         if (events < 0) {
294                 log_error("Could not get monitor events: %s", strerror(-r));
295                 goto out;
296         }
297
298         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
299                             m);
300         if (r < 0) {
301                 log_error("Could not add io event source: %s", strerror(-r));
302                 goto out;
303         }
304
305         r = sd_rtnl_open(&m->rtnl, RTMGRP_LINK);
306         if (r < 0) {
307                 log_error("Could not create rtnl: %s", strerror(-r));
308                 goto out;
309         }
310
311         r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
312         if (r < 0) {
313                 log_error("Could not attach event to rtnl: %s", strerror(-r));
314                 return r;
315         }
316
317         r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, newlink_event_handler, m);
318         if (r < 0)
319                 return r;
320
321         if (all_configured(m)) {
322                 r = 0;
323                 goto out;
324         }
325
326         sd_notify(false,
327                   "READY=1\n"
328                   "STATUS=Waiting for network connections...");
329
330         r = sd_event_loop(m->event);
331         if (r < 0) {
332                 log_error("Event loop failed: %s", strerror(-r));
333                 goto out;
334         }
335
336 out:
337         sd_notify(false,
338                   "STATUS=All interfaces configured...");
339
340         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
341 }