Added support of WPA3-SAE security mode.
[platform/upstream/connman.git] / src / connmand-wait-online.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2015  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <glib.h>
29 #include <errno.h>
30 #include <stdbool.h>
31 #include <dbus/dbus.h>
32
33 #include <gdbus.h>
34 #include <connman/dbus.h>
35
36 static DBusConnection *connection;
37 static GMainLoop *main_loop;
38 static int timeout = 0;
39 static int exit_value = 0;
40
41 static gboolean option_version = FALSE;
42 static gchar *option_interface = NULL;
43 static gchar *option_ignore = NULL;
44 static gint option_timeout = 120;
45
46 struct devices {
47         char **interface;
48         char **ignore;
49 };
50
51 static GOptionEntry options[] = {
52         { "interface", 'i', 0, G_OPTION_ARG_STRING, &option_interface,
53           "Specify networking device or interface", "DEV" },
54         { "ignore", 'I', 0, G_OPTION_ARG_STRING, &option_ignore,
55           "Specify networking device or interface to ignore", "DEV" },
56         { "timeout", 0, 0, G_OPTION_ARG_INT, &option_timeout,
57           "Time to wait for network going online. Default is 120 seconds.",
58           "seconds" },
59         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
60           "Show version information and exit" },
61         { NULL },
62 };
63
64 static bool compare_interface(const char *interface, struct devices *devices)
65 {
66         int i;
67
68         if (!interface || !devices)
69                 return false;
70
71         for (i = 0; devices->ignore && devices->ignore[i]; i++)
72                 if (!strcmp(interface, devices->ignore[i]))
73                         return false;
74
75         if (!devices->interface)
76                 return true;
77
78         for (i = 0; devices->interface[i]; i++)
79                 if (!strcmp(interface, devices->interface[i]))
80                         return true;
81
82         return false;
83 }
84
85 static bool state_online(DBusMessageIter *iter)
86 {
87         char *str;
88         DBusMessageIter variant;
89
90         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
91                 return false;
92
93         dbus_message_iter_get_basic(iter, &str);
94         if (strcmp(str, "State"))
95                 return false;
96
97         dbus_message_iter_next(iter);
98
99         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
100                 return false;
101
102         dbus_message_iter_recurse(iter, &variant);
103
104         if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING)
105                 return false;
106
107         dbus_message_iter_get_basic(&variant, &str);
108         if (strcmp(str, "ready") && strcmp(str, "online"))
109                 return false;
110
111         return true;
112 }
113
114 static bool service_properties_online(DBusMessageIter *array_entry,
115                                 struct devices *devices)
116 {
117         bool interface = !devices;
118         bool state = false;
119         DBusMessageIter dict, dict_entry, variant, eth_array, eth_dict,
120                 eth_variant;
121         char *str;
122
123         for (dbus_message_iter_recurse(array_entry, &dict);
124              dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY;
125              dbus_message_iter_next(&dict)) {
126
127                 dbus_message_iter_recurse(&dict, &dict_entry);
128                 if (dbus_message_iter_get_arg_type(&dict_entry)
129                                 != DBUS_TYPE_STRING)
130                         continue;
131
132                 if (state_online(&dict_entry)) {
133                         state = true;
134                         continue;
135                 }
136
137                 dbus_message_iter_recurse(&dict, &dict_entry);
138
139                 dbus_message_iter_get_basic(&dict_entry, &str);
140
141                 if (devices && !strcmp(str, "Ethernet")) {
142                         dbus_message_iter_next(&dict_entry);
143
144                         if (dbus_message_iter_get_arg_type(&dict_entry)
145                                         != DBUS_TYPE_VARIANT)
146                                 break;
147
148                         dbus_message_iter_recurse(&dict_entry, &variant);
149                         if (dbus_message_iter_get_arg_type(&variant)
150                                         != DBUS_TYPE_ARRAY)
151                                 break;
152
153                         for (dbus_message_iter_recurse(&variant, &eth_array);
154                              dbus_message_iter_get_arg_type(&eth_array)
155                                      == DBUS_TYPE_DICT_ENTRY;
156                              dbus_message_iter_next(&eth_array)) {
157
158                                 dbus_message_iter_recurse(&eth_array, &eth_dict);
159
160                                 if (dbus_message_iter_get_arg_type(&eth_dict)
161                                                 != DBUS_TYPE_STRING)
162                                         continue;
163
164                                 dbus_message_iter_get_basic(&eth_dict, &str);
165                                 if (!strcmp(str, "Interface")) {
166
167                                         dbus_message_iter_next(&eth_dict);
168                                         if (dbus_message_iter_get_arg_type(&eth_dict)
169                                                         != DBUS_TYPE_VARIANT)
170                                                 break;
171
172                                         dbus_message_iter_recurse(&eth_dict,
173                                                                 &eth_variant);
174                                         if (dbus_message_iter_get_arg_type(&eth_variant)
175                                                         != DBUS_TYPE_STRING)
176                                                 break;
177
178                                         dbus_message_iter_get_basic(&eth_variant,
179                                                                 &str);
180                                         interface = compare_interface(str,
181                                                                 devices);
182
183                                         break;
184                                 }
185                         }
186                 }
187
188                 if (state && interface) {
189                         g_main_loop_quit(main_loop);
190                         return true;
191                 }
192         }
193
194         return false;
195 }
196
197 static void services_dict_online(DBusMessageIter *iter, struct devices *devices)
198 {
199         DBusMessageIter array, array_entry;
200
201         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
202                 return;
203
204         for (dbus_message_iter_recurse(iter, &array);
205              dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT;
206              dbus_message_iter_next(&array)) {
207
208                 dbus_message_iter_recurse(&array, &array_entry);
209
210                 if (dbus_message_iter_get_arg_type(&array_entry) !=
211                                 DBUS_TYPE_OBJECT_PATH)
212                         break;
213
214                 dbus_message_iter_next(&array_entry);
215
216                 if (dbus_message_iter_get_arg_type(&array_entry) !=
217                                 DBUS_TYPE_ARRAY)
218                         continue;
219
220                 if (service_properties_online(&array_entry, devices))
221                         break;
222         }
223 }
224
225 static void manager_get_services_return(DBusPendingCall *call,
226                                         void *user_data)
227 {
228         struct devices *devices = user_data;
229         DBusMessage *reply;
230         DBusMessageIter iter;
231
232         reply = dbus_pending_call_steal_reply(call);
233         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
234                 goto fail;
235
236         if (!dbus_message_iter_init(reply, &iter))
237                 goto fail;
238
239         services_dict_online(&iter, devices);
240
241 fail:
242         dbus_message_unref(reply);
243         dbus_pending_call_unref(call);
244 }
245
246 static void manager_get_services(struct devices *devices)
247 {
248         DBusMessage *message;
249         DBusPendingCall *call;
250
251         message = dbus_message_new_method_call(CONNMAN_SERVICE,
252                                         CONNMAN_MANAGER_PATH,
253                                         CONNMAN_MANAGER_INTERFACE,
254                                         "GetServices");
255         if (!message)
256                 return;
257
258         if (!dbus_connection_send_with_reply(connection, message, &call, -1))
259                 goto fail;
260
261         if (!call)
262                 goto fail;
263
264         dbus_pending_call_set_notify(call, manager_get_services_return,
265                                 devices, NULL);
266
267 fail:
268         dbus_message_unref(message);
269 }
270
271 static void manager_properties_online(DBusMessageIter *iter,
272                                 struct devices *devices)
273 {
274         DBusMessageIter array, dict_entry;
275
276         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
277                 return;
278
279         for (dbus_message_iter_recurse(iter, &array);
280              dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY;
281              dbus_message_iter_next(&array)) {
282
283                 dbus_message_iter_recurse(&array, &dict_entry);
284
285                 if (state_online(&dict_entry)) {
286                         if (devices)
287                                 manager_get_services(devices);
288                         else
289                                 g_main_loop_quit(main_loop);
290
291                         break;
292                 }
293         }
294 }
295
296 static void manager_get_properties_return(DBusPendingCall *call, void *user_data)
297 {
298         struct devices *devices = user_data;
299         DBusMessage *reply;
300         DBusMessageIter iter;
301
302         reply = dbus_pending_call_steal_reply(call);
303         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
304                 goto fail;
305
306         if (!dbus_message_iter_init(reply, &iter))
307                 goto fail;
308
309         manager_properties_online(&iter, devices);
310
311 fail:
312         dbus_message_unref(reply);
313         dbus_pending_call_unref(call);
314 }
315
316 static void manager_get_properties(struct devices *devices)
317 {
318         DBusMessage *message;
319         DBusPendingCall *call;
320
321         message = dbus_message_new_method_call(CONNMAN_SERVICE,
322                                         CONNMAN_MANAGER_PATH,
323                                         CONNMAN_MANAGER_INTERFACE,
324                                         "GetProperties");
325         if (!message)
326                 return;
327
328         if (!dbus_connection_send_with_reply(connection, message, &call, -1))
329                 goto fail;
330
331         if (!call)
332                 goto fail;
333
334         dbus_pending_call_set_notify(call, manager_get_properties_return,
335                                 devices, NULL);
336
337 fail:
338         dbus_message_unref(message);
339 }
340
341 static DBusHandlerResult manager_property_changed(DBusConnection *connection,
342                 DBusMessage *message, void *user_data)
343 {
344         struct devices *devices = user_data;
345         DBusMessageIter iter;
346
347         if (dbus_message_is_signal(message, CONNMAN_MANAGER_INTERFACE,
348                                         "PropertyChanged")) {
349                 dbus_message_iter_init(message, &iter);
350
351                 if (state_online(&iter)) {
352                         if (devices)
353                                 manager_get_services(devices);
354                         else
355                                 g_main_loop_quit(main_loop);
356                 }
357         }
358
359         return DBUS_HANDLER_RESULT_HANDLED;
360 }
361
362 static gboolean timeout_triggered(gpointer user_data)
363 {
364         exit_value = -ETIMEDOUT;
365         g_main_loop_quit(main_loop);
366         timeout = 0;
367
368         return FALSE;
369 }
370
371 int main(int argc, char *argv[])
372 {
373         const char *filter = "type='signal',interface='"
374                 CONNMAN_MANAGER_INTERFACE "'";
375         int err = 0;
376         GError *g_err = NULL;
377         struct devices devices = { NULL, NULL };
378         DBusError dbus_err;
379         GOptionContext *context;
380
381         context = g_option_context_new(NULL);
382         g_option_context_add_main_entries(context, options, NULL);
383
384         if (!g_option_context_parse(context, &argc, &argv, &g_err)) {
385                 if (g_err) {
386                         fprintf(stderr, "%s\n", g_err->message);
387                         g_error_free(g_err);
388                 } else
389                         fprintf(stderr, "An unknown error occurred\n");
390
391                 return EOPNOTSUPP;
392         }
393
394         g_option_context_free(context);
395
396         if (option_interface) {
397                 devices.interface = g_strsplit(option_interface, ",", -1);
398                 g_free(option_interface);
399         }
400
401         if (option_ignore) {
402                 devices.ignore = g_strsplit(option_ignore, ",", -1);
403                 g_free(option_ignore);
404         }
405
406         if (option_version) {
407                 fprintf(stdout, "%s\n", VERSION);
408                 goto free;
409         }
410
411         dbus_error_init(&dbus_err);
412         connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &dbus_err);
413
414         if (dbus_error_is_set(&dbus_err)) {
415                 fprintf(stderr, "Error: %s\n", dbus_err.message);
416
417                 err = -ENOPROTOOPT;
418                 goto fail;
419         }
420
421         main_loop = g_main_loop_new(NULL, FALSE);
422
423         dbus_connection_add_filter(connection, manager_property_changed,
424                                 &devices, NULL);
425
426         dbus_bus_add_match(connection, filter, &dbus_err);
427
428         if (dbus_error_is_set(&dbus_err)) {
429                 fprintf(stderr, "Error: %s\n", dbus_err.message);
430
431                 err = -ENOPROTOOPT;
432                 goto cleanup;
433         }
434
435         if (option_timeout)
436                 timeout = g_timeout_add_seconds(option_timeout,
437                                                 timeout_triggered, NULL);
438
439         manager_get_properties(&devices);
440
441         g_main_loop_run(main_loop);
442         err = exit_value;
443
444 cleanup:
445         dbus_bus_remove_match(connection, filter, NULL);
446         dbus_connection_remove_filter(connection, manager_property_changed,
447                                 &devices);
448
449         dbus_connection_unref(connection);
450         g_main_loop_unref(main_loop);
451
452 fail:
453         dbus_error_free(&dbus_err);
454 free:
455         g_strfreev(devices.interface);
456         g_strfreev(devices.ignore);
457         if (timeout)
458                 g_source_remove(timeout);
459
460         return -err;
461 }