chiark / gitweb /
a4729f464f12efb322d1a2313ff561a10ecbc6a0
[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 != -EUNATCH && (r < 0 || !streq(state, "configured"))) {
182                         /* managed by networkd, but not yet configured */
183                         return false;
184                 }
185
186                 r = sd_network_get_link_operational_state(indices[i], &oper_state);
187                 if (r >= 0 && streq(oper_state, "carrier"))
188                         /* we wait for at least one link to be ready,
189                            regardless of who manages it */
190                         one_ready = true;
191         }
192
193         return one_ready;
194 }
195
196 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
197                          void *userdata) {
198         Manager *m = userdata;
199
200         assert(m);
201         assert(m->event);
202
203         if (all_configured(m))
204                 sd_event_exit(m->event, 0);
205
206         return 1;
207 }
208
209 void manager_free(Manager *m) {
210         if (!m)
211                 return;
212
213         sd_event_unref(m->event);
214         sd_rtnl_unref(m->rtnl);
215
216         free(m);
217 }
218
219 int main(int argc, char *argv[]) {
220         _cleanup_manager_free_ Manager *m = NULL;
221         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
222         _cleanup_network_monitor_unref_ sd_network_monitor *monitor = 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_network_monitor_new(NULL, &monitor);
242         if (r < 0) {
243                 log_error("Could not create monitor: %s", strerror(-r));
244                 goto out;
245         }
246
247         r = sd_event_new(&m->event);
248         if (r < 0) {
249                 log_error("Could not create event: %s", strerror(-r));
250                 goto out;
251         }
252
253         fd = sd_network_monitor_get_fd(monitor);
254         if (fd < 0) {
255                 log_error("Could not get monitor fd: %s", strerror(-r));
256                 goto out;
257         }
258
259         events = sd_network_monitor_get_events(monitor);
260         if (events < 0) {
261                 log_error("Could not get monitor events: %s", strerror(-r));
262                 goto out;
263         }
264
265         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
266                             m);
267         if (r < 0) {
268                 log_error("Could not add io event source: %s", strerror(-r));
269                 goto out;
270         }
271
272         r = sd_rtnl_open(&m->rtnl, 0);
273         if (r < 0) {
274                 log_error("Could not create rtnl: %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 }