dundee: Update network connected status
[platform/upstream/connman.git] / plugins / dundee.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  BMW Car IT GmbH. 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 <string.h>
27 #include <errno.h>
28
29 #include <gdbus.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/device.h>
34 #include <connman/network.h>
35 #include <connman/inet.h>
36 #include <connman/dbus.h>
37
38 #define DUNDEE_SERVICE                  "org.ofono.dundee"
39 #define DUNDEE_MANAGER_INTERFACE        DUNDEE_SERVICE ".Manager"
40 #define DUNDEE_DEVICE_INTERFACE         DUNDEE_SERVICE ".Device"
41
42 #define DEVICE_ADDED                    "DeviceAdded"
43 #define DEVICE_REMOVED                  "DeviceRemoved"
44 #define PROPERTY_CHANGED                "PropertyChanged"
45
46 #define GET_PROPERTIES                  "GetProperties"
47 #define GET_DEVICES                     "GetDevices"
48
49 #define TIMEOUT 40000
50
51 static DBusConnection *connection;
52
53 static GHashTable *dundee_devices = NULL;
54
55 struct dundee_data {
56         char *path;
57         char *name;
58
59         struct connman_device *device;
60         struct connman_network *network;
61
62         connman_bool_t active;
63
64         int index;
65
66         /* IPv4 Settings */
67         enum connman_ipconfig_method method;
68         struct connman_ipaddress *address;
69         char *nameservers;
70 };
71
72 static char *get_ident(const char *path)
73 {
74         char *pos;
75
76         if (*path != '/')
77                 return NULL;
78
79         pos = strrchr(path, '/');
80         if (pos == NULL)
81                 return NULL;
82
83         return pos + 1;
84 }
85
86 static void create_device(struct dundee_data *info)
87 {
88         struct connman_device *device;
89         char *ident;
90
91         DBG("%s", info->path);
92
93         ident = g_strdup(get_ident(info->path));
94         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
95         if (device == NULL)
96                 goto out;
97
98         DBG("device %p", device);
99
100         connman_device_set_ident(device, ident);
101
102         connman_device_set_string(device, "Path", info->path);
103
104         connman_device_set_data(device, info);
105
106         if (connman_device_register(device) < 0) {
107                 connman_error("Failed to register DUN device");
108                 connman_device_unref(device);
109                 goto out;
110         }
111
112         info->device = device;
113
114 out:
115         g_free(ident);
116 }
117
118 static void destroy_device(struct dundee_data *info)
119 {
120         connman_device_set_powered(info->device, FALSE);
121
122         if (info->network != NULL) {
123                 connman_device_remove_network(info->device, info->network);
124                 connman_network_unref(info->network);
125                 info->network = NULL;
126         }
127
128         connman_device_unregister(info->device);
129         connman_device_unref(info->device);
130
131         info->device = NULL;
132 }
133
134 static void device_destroy(gpointer data)
135 {
136         struct dundee_data *info = data;
137
138         if (info->device != NULL)
139                 destroy_device(info);
140
141         g_free(info->path);
142         g_free(info->name);
143
144         g_free(info);
145 }
146
147 static void create_network(struct dundee_data *info)
148 {
149         struct connman_network *network;
150         const char *group;
151
152         DBG("%s", info->path);
153
154         network = connman_network_create(info->path,
155                                 CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
156         if (network == NULL)
157                 return;
158
159         DBG("network %p", network);
160
161         connman_network_set_data(network, info);
162
163         connman_network_set_string(network, "Path",
164                                 info->path);
165
166         connman_network_set_name(network, info->name);
167
168         group = get_ident(info->path);
169         connman_network_set_group(network, group);
170
171         connman_network_set_available(network, TRUE);
172
173         if (connman_device_add_network(info->device, network) < 0) {
174                 connman_network_unref(network);
175                 return;
176         }
177
178         info->network = network;
179 }
180
181 static void set_connected(struct dundee_data *info)
182 {
183         DBG("%s", info->path);
184
185         connman_inet_ifup(info->index);
186
187         connman_network_set_index(info->network, info->index);
188         connman_network_set_ipv4_method(info->network,
189                                         CONNMAN_IPCONFIG_METHOD_FIXED);
190         connman_network_set_ipaddress(info->network, info->address);
191         connman_network_set_nameservers(info->network, info->nameservers);
192
193         connman_network_set_connected(info->network, TRUE);
194 }
195
196 static void set_disconnected(struct dundee_data *info)
197 {
198         DBG("%s", info->path);
199
200         connman_network_set_connected(info->network, FALSE);
201         connman_inet_ifdown(info->index);
202 }
203
204 static int network_probe(struct connman_network *network)
205 {
206         DBG("network %p", network);
207
208         return 0;
209 }
210
211 static void network_remove(struct connman_network *network)
212 {
213         DBG("network %p", network);
214 }
215
216 static int network_connect(struct connman_network *network)
217 {
218         DBG("network %p", network);
219
220         return 0;
221 }
222
223 static int network_disconnect(struct connman_network *network)
224 {
225         DBG("network %p", network);
226
227         return 0;
228 }
229
230 static struct connman_network_driver network_driver = {
231         .name           = "network",
232         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
233         .probe          = network_probe,
234         .remove         = network_remove,
235         .connect        = network_connect,
236         .disconnect     = network_disconnect,
237 };
238
239 static int dundee_probe(struct connman_device *device)
240 {
241         DBG("device %p", device);
242
243         return 0;
244 }
245
246 static void dundee_remove(struct connman_device *device)
247 {
248         DBG("device %p", device);
249 }
250
251 static int dundee_enable(struct connman_device *device)
252 {
253         DBG("device %p", device);
254
255         return 0;
256 }
257
258 static int dundee_disable(struct connman_device *device)
259 {
260         DBG("device %p", device);
261
262         return 0;
263 }
264
265 static struct connman_device_driver dundee_driver = {
266         .name           = "dundee",
267         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
268         .probe          = dundee_probe,
269         .remove         = dundee_remove,
270         .enable         = dundee_enable,
271         .disable        = dundee_disable,
272 };
273
274 static char *extract_nameservers(DBusMessageIter *array)
275 {
276         DBusMessageIter entry;
277         char *nameservers = NULL;
278         char *tmp;
279
280         dbus_message_iter_recurse(array, &entry);
281
282         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
283                 const char *nameserver;
284
285                 dbus_message_iter_get_basic(&entry, &nameserver);
286
287                 if (nameservers == NULL) {
288                         nameservers = g_strdup(nameserver);
289                 } else {
290                         tmp = nameservers;
291                         nameservers = g_strdup_printf("%s %s", tmp, nameserver);
292                         g_free(tmp);
293                 }
294
295                 dbus_message_iter_next(&entry);
296         }
297
298         return nameservers;
299 }
300
301 static void extract_settings(DBusMessageIter *array,
302                                 struct dundee_data *info)
303 {
304         DBusMessageIter dict;
305         char *address = NULL, *gateway = NULL;
306         char *nameservers = NULL;
307         const char *interface = NULL;
308         int index = -1;
309
310         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
311                 return;
312
313         dbus_message_iter_recurse(array, &dict);
314
315         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
316                 DBusMessageIter entry, value;
317                 const char *key, *val;
318
319                 dbus_message_iter_recurse(&dict, &entry);
320                 dbus_message_iter_get_basic(&entry, &key);
321
322                 dbus_message_iter_next(&entry);
323                 dbus_message_iter_recurse(&entry, &value);
324
325                 if (g_str_equal(key, "Interface") == TRUE) {
326                         dbus_message_iter_get_basic(&value, &interface);
327
328                         DBG("Interface %s", interface);
329
330                         index = connman_inet_ifindex(interface);
331
332                         DBG("index %d", index);
333
334                         if (index < 0)
335                                 break;
336                 } else if (g_str_equal(key, "Address") == TRUE) {
337                         dbus_message_iter_get_basic(&value, &val);
338
339                         address = g_strdup(val);
340
341                         DBG("Address %s", address);
342                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
343                         nameservers = extract_nameservers(&value);
344
345                         DBG("Nameservers %s", nameservers);
346                 } else if (g_str_equal(key, "Gateway") == TRUE) {
347                         dbus_message_iter_get_basic(&value, &val);
348
349                         gateway = g_strdup(val);
350
351                         DBG("Gateway %s", gateway);
352                 }
353
354                 dbus_message_iter_next(&dict);
355         }
356
357         if (index < 0)
358                 goto out;
359
360         info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
361         if (info->address == NULL)
362                 goto out;
363
364         info->index = index;
365         connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
366
367         info->nameservers = nameservers;
368
369 out:
370         if (info->nameservers != nameservers)
371                 g_free(nameservers);
372
373         g_free(address);
374         g_free(gateway);
375 }
376
377 static gboolean device_changed(DBusConnection *connection,
378                                 DBusMessage *message,
379                                 void *user_data)
380 {
381         const char *path = dbus_message_get_path(message);
382         struct dundee_data *info = NULL;
383         DBusMessageIter iter, value;
384         const char *key;
385         const char *signature = DBUS_TYPE_STRING_AS_STRING
386                 DBUS_TYPE_VARIANT_AS_STRING;
387
388         if (dbus_message_has_signature(message, signature) == FALSE) {
389                 connman_error("dundee signature does not match");
390                 return TRUE;
391         }
392
393         info = g_hash_table_lookup(dundee_devices, path);
394         if (info == NULL)
395                 return TRUE;
396
397         if (dbus_message_iter_init(message, &iter) == FALSE)
398                 return TRUE;
399
400         dbus_message_iter_get_basic(&iter, &key);
401
402         dbus_message_iter_next(&iter);
403         dbus_message_iter_recurse(&iter, &value);
404
405         /*
406          * Dundee guarantees the ordering of Settings and
407          * Active. Settings will always be send before Active = True.
408          * That means we don't have to order here.
409          */
410         if (g_str_equal(key, "Active") == TRUE) {
411                 dbus_message_iter_get_basic(&value, &info->active);
412
413                 DBG("%s Active %d", info->path, info->active);
414
415                 if (info->active == TRUE)
416                         set_connected(info);
417                 else
418                         set_disconnected(info);
419         } else if (g_str_equal(key, "Settings") == TRUE) {
420                 DBG("%s Settings", info->path);
421
422                 extract_settings(&value, info);
423         } else if (g_str_equal(key, "Name") == TRUE) {
424                 char *name;
425
426                 dbus_message_iter_get_basic(&value, &name);
427
428                 g_free(info->name);
429                 info->name = g_strdup(name);
430
431                 DBG("%s Name %s", info->path, info->name);
432
433                 connman_network_set_name(info->network, info->name);
434                 connman_network_update(info->network);
435         }
436
437         return TRUE;
438 }
439
440 static void add_device(const char *path, DBusMessageIter *properties)
441 {
442         struct dundee_data *info;
443
444         info = g_hash_table_lookup(dundee_devices, path);
445         if (info != NULL)
446                 return;
447
448         info = g_try_new0(struct dundee_data, 1);
449         if (info == NULL)
450                 return;
451
452         info->path = g_strdup(path);
453
454         while (dbus_message_iter_get_arg_type(properties) ==
455                         DBUS_TYPE_DICT_ENTRY) {
456                 DBusMessageIter entry, value;
457                 const char *key;
458
459                 dbus_message_iter_recurse(properties, &entry);
460                 dbus_message_iter_get_basic(&entry, &key);
461
462                 dbus_message_iter_next(&entry);
463                 dbus_message_iter_recurse(&entry, &value);
464
465                 if (g_str_equal(key, "Active") == TRUE) {
466                         dbus_message_iter_get_basic(&value, &info->active);
467
468                         DBG("%s Active %d", info->path, info->active);
469                 } else if (g_str_equal(key, "Settings") == TRUE) {
470                         DBG("%s Settings", info->path);
471
472                         extract_settings(&value, info);
473                 } else if (g_str_equal(key, "Name") == TRUE) {
474                         char *name;
475
476                         dbus_message_iter_get_basic(&value, &name);
477
478                         info->name = g_strdup(name);
479
480                         DBG("%s Name %s", info->path, info->name);
481                 }
482
483                 dbus_message_iter_next(properties);
484         }
485
486         g_hash_table_insert(dundee_devices, g_strdup(path), info);
487
488         create_device(info);
489         create_network(info);
490
491         if (info->active == TRUE)
492                 set_connected(info);
493 }
494
495 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
496                                 void *user_data)
497 {
498         DBusMessageIter iter, properties;
499         const char *path;
500         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
501                 DBUS_TYPE_ARRAY_AS_STRING
502                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
503                 DBUS_TYPE_STRING_AS_STRING
504                 DBUS_TYPE_VARIANT_AS_STRING
505                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
506
507         if (dbus_message_has_signature(message, signature) == FALSE) {
508                 connman_error("dundee signature does not match");
509                 return TRUE;
510         }
511
512         DBG("");
513
514         if (dbus_message_iter_init(message, &iter) == FALSE)
515                 return TRUE;
516
517         dbus_message_iter_get_basic(&iter, &path);
518
519         dbus_message_iter_next(&iter);
520         dbus_message_iter_recurse(&iter, &properties);
521
522         add_device(path, &properties);
523
524         return TRUE;
525 }
526
527 static void remove_device(DBusConnection *connection, const char *path)
528 {
529         DBG("path %s", path);
530
531         g_hash_table_remove(dundee_devices, path);
532 }
533
534 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
535                                 void *user_data)
536 {
537         const char *path;
538         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
539
540         if (dbus_message_has_signature(message, signature) == FALSE) {
541                 connman_error("dundee signature does not match");
542                 return TRUE;
543         }
544
545         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
546                                 DBUS_TYPE_INVALID);
547         remove_device(connection, path);
548         return TRUE;
549 }
550
551 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
552 {
553         DBusMessage *reply;
554         DBusError error;
555         DBusMessageIter array, dict;
556         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
557                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
558                 DBUS_TYPE_OBJECT_PATH_AS_STRING
559                 DBUS_TYPE_ARRAY_AS_STRING
560                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
561                 DBUS_TYPE_STRING_AS_STRING
562                 DBUS_TYPE_VARIANT_AS_STRING
563                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
564                 DBUS_STRUCT_END_CHAR_AS_STRING;
565
566         DBG("");
567
568         reply = dbus_pending_call_steal_reply(call);
569
570         if (dbus_message_has_signature(reply, signature) == FALSE) {
571                 connman_error("dundee signature does not match");
572                 goto done;
573         }
574
575         dbus_error_init(&error);
576
577         if (dbus_set_error_from_message(&error, reply) == TRUE) {
578                 connman_error("%s", error.message);
579                 dbus_error_free(&error);
580                 goto done;
581         }
582
583         if (dbus_message_iter_init(reply, &array) == FALSE)
584                 goto done;
585
586         dbus_message_iter_recurse(&array, &dict);
587
588         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
589                 DBusMessageIter value, properties;
590                 const char *path;
591
592                 dbus_message_iter_recurse(&dict, &value);
593                 dbus_message_iter_get_basic(&value, &path);
594
595                 dbus_message_iter_next(&value);
596                 dbus_message_iter_recurse(&value, &properties);
597
598                 add_device(path, &properties);
599
600                 dbus_message_iter_next(&dict);
601         }
602
603 done:
604         dbus_message_unref(reply);
605
606         dbus_pending_call_unref(call);
607 }
608
609 static int manager_get_devices(void)
610 {
611         DBusMessage *message;
612         DBusPendingCall *call;
613
614         DBG("");
615
616         message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
617                                         DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
618         if (message == NULL)
619                 return -ENOMEM;
620
621         if (dbus_connection_send_with_reply(connection, message,
622                                                 &call, TIMEOUT) == FALSE) {
623                 connman_error("Failed to call GetDevices()");
624                 dbus_message_unref(message);
625                 return -EINVAL;
626         }
627
628         if (call == NULL) {
629                 connman_error("D-Bus connection not available");
630                 dbus_message_unref(message);
631                 return -EINVAL;
632         }
633
634         dbus_pending_call_set_notify(call, manager_get_devices_reply,
635                                         NULL, NULL);
636
637         dbus_message_unref(message);
638
639         return -EINPROGRESS;
640 }
641
642 static void dundee_connect(DBusConnection *connection, void *user_data)
643 {
644         DBG("connection %p", connection);
645
646         dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
647                                         g_free, device_destroy);
648
649         manager_get_devices();
650 }
651
652 static void dundee_disconnect(DBusConnection *connection, void *user_data)
653 {
654         DBG("connection %p", connection);
655
656         g_hash_table_destroy(dundee_devices);
657         dundee_devices = NULL;
658 }
659
660 static guint watch;
661 static guint added_watch;
662 static guint removed_watch;
663 static guint device_watch;
664
665 static int dundee_init(void)
666 {
667         int err;
668
669         connection = connman_dbus_get_connection();
670         if (connection == NULL)
671                 return -EIO;
672
673         watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
674                         dundee_connect, dundee_disconnect, NULL, NULL);
675
676         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
677                                                 DUNDEE_MANAGER_INTERFACE,
678                                                 DEVICE_ADDED, device_added,
679                                                 NULL, NULL);
680
681         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
682                                                 DUNDEE_MANAGER_INTERFACE,
683                                                 DEVICE_REMOVED, device_removed,
684                                                 NULL, NULL);
685
686         device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
687                                                 DUNDEE_DEVICE_INTERFACE,
688                                                 PROPERTY_CHANGED,
689                                                 device_changed,
690                                                 NULL, NULL);
691
692
693         if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
694                         device_watch == 0) {
695                 err = -EIO;
696                 goto remove;
697         }
698
699         err = connman_network_driver_register(&network_driver);
700         if (err < 0)
701                 goto remove;
702
703         err = connman_device_driver_register(&dundee_driver);
704         if (err < 0) {
705                 connman_network_driver_unregister(&network_driver);
706                 goto remove;
707         }
708
709         return 0;
710
711 remove:
712         g_dbus_remove_watch(connection, watch);
713         g_dbus_remove_watch(connection, added_watch);
714         g_dbus_remove_watch(connection, removed_watch);
715         g_dbus_remove_watch(connection, device_watch);
716
717         dbus_connection_unref(connection);
718
719         return err;
720 }
721
722 static void dundee_exit(void)
723 {
724         g_dbus_remove_watch(connection, watch);
725         g_dbus_remove_watch(connection, added_watch);
726         g_dbus_remove_watch(connection, removed_watch);
727         g_dbus_remove_watch(connection, device_watch);
728
729         connman_device_driver_unregister(&dundee_driver);
730         connman_network_driver_unregister(&network_driver);
731
732         dbus_connection_unref(connection);
733 }
734
735 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
736                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)