chiark / gitweb /
networkd: netdev - add dummy support
[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("could 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 &&
187                     (streq(oper_state, "degraded") ||
188                      streq(oper_state, "routable")))
189                         /* we wait for at least one link to be ready,
190                            regardless of who manages it */
191                         one_ready = true;
192         }
193
194         return one_ready;
195 }
196
197 static int monitor_event_handler(sd_event_source *s, int fd, uint32_t revents,
198                          void *userdata) {
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
207         sd_network_monitor_flush(m->monitor);
208
209         return 1;
210 }
211
212 void manager_free(Manager *m) {
213         if (!m)
214                 return;
215
216         sd_event_unref(m->event);
217         sd_rtnl_unref(m->rtnl);
218
219         free(m);
220 }
221
222 int main(int argc, char *argv[]) {
223         _cleanup_manager_free_ Manager *m = NULL;
224         _cleanup_event_source_unref_ sd_event_source *event_source = NULL;
225         int r, fd, events;
226
227         umask(0022);
228
229         log_parse_environment();
230         log_open();
231
232         r = parse_argv(argc, argv);
233         if (r <= 0)
234                 return r;
235
236         if (arg_quiet)
237                 log_set_max_level(LOG_WARNING);
238
239         m = new0(Manager, 1);
240         if (!m)
241                 return log_oom();
242
243         r = sd_event_new(&m->event);
244         if (r < 0) {
245                 log_error("Could not create event: %s", strerror(-r));
246                 goto out;
247         }
248
249         r = sd_rtnl_open(&m->rtnl, 0);
250         if (r < 0) {
251                 log_error("Could not create rtnl: %s", strerror(-r));
252                 goto out;
253         }
254
255         r = sd_network_monitor_new(NULL, &m->monitor);
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);
262         if (fd < 0) {
263                 log_error("Could not get monitor fd: %s", strerror(-r));
264                 goto out;
265         }
266
267         events = sd_network_monitor_get_events(m->monitor);
268         if (events < 0) {
269                 log_error("Could not get monitor events: %s", strerror(-r));
270                 goto out;
271         }
272
273         r = sd_event_add_io(m->event, &event_source, fd, events, &monitor_event_handler,
274                             m);
275         if (r < 0) {
276                 log_error("Could not add io event source: %s", strerror(-r));
277                 goto out;
278         }
279
280         if (all_configured(m)) {
281                 r = 0;
282                 goto out;
283         }
284
285         sd_notify(false,
286                   "READY=1\n"
287                   "STATUS=Waiting for network connections...");
288
289         r = sd_event_loop(m->event);
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
299         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
300 }