dundee: Register connman_network at core
[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 int network_probe(struct connman_network *network)
182 {
183         DBG("network %p", network);
184
185         return 0;
186 }
187
188 static void network_remove(struct connman_network *network)
189 {
190         DBG("network %p", network);
191 }
192
193 static int network_connect(struct connman_network *network)
194 {
195         DBG("network %p", network);
196
197         return 0;
198 }
199
200 static int network_disconnect(struct connman_network *network)
201 {
202         DBG("network %p", network);
203
204         return 0;
205 }
206
207 static struct connman_network_driver network_driver = {
208         .name           = "network",
209         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
210         .probe          = network_probe,
211         .remove         = network_remove,
212         .connect        = network_connect,
213         .disconnect     = network_disconnect,
214 };
215
216 static int dundee_probe(struct connman_device *device)
217 {
218         DBG("device %p", device);
219
220         return 0;
221 }
222
223 static void dundee_remove(struct connman_device *device)
224 {
225         DBG("device %p", device);
226 }
227
228 static int dundee_enable(struct connman_device *device)
229 {
230         DBG("device %p", device);
231
232         return 0;
233 }
234
235 static int dundee_disable(struct connman_device *device)
236 {
237         DBG("device %p", device);
238
239         return 0;
240 }
241
242 static struct connman_device_driver dundee_driver = {
243         .name           = "dundee",
244         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
245         .probe          = dundee_probe,
246         .remove         = dundee_remove,
247         .enable         = dundee_enable,
248         .disable        = dundee_disable,
249 };
250
251 static char *extract_nameservers(DBusMessageIter *array)
252 {
253         DBusMessageIter entry;
254         char *nameservers = NULL;
255         char *tmp;
256
257         dbus_message_iter_recurse(array, &entry);
258
259         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
260                 const char *nameserver;
261
262                 dbus_message_iter_get_basic(&entry, &nameserver);
263
264                 if (nameservers == NULL) {
265                         nameservers = g_strdup(nameserver);
266                 } else {
267                         tmp = nameservers;
268                         nameservers = g_strdup_printf("%s %s", tmp, nameserver);
269                         g_free(tmp);
270                 }
271
272                 dbus_message_iter_next(&entry);
273         }
274
275         return nameservers;
276 }
277
278 static void extract_settings(DBusMessageIter *array,
279                                 struct dundee_data *info)
280 {
281         DBusMessageIter dict;
282         char *address = NULL, *gateway = NULL;
283         char *nameservers = NULL;
284         const char *interface = NULL;
285         int index = -1;
286
287         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
288                 return;
289
290         dbus_message_iter_recurse(array, &dict);
291
292         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
293                 DBusMessageIter entry, value;
294                 const char *key, *val;
295
296                 dbus_message_iter_recurse(&dict, &entry);
297                 dbus_message_iter_get_basic(&entry, &key);
298
299                 dbus_message_iter_next(&entry);
300                 dbus_message_iter_recurse(&entry, &value);
301
302                 if (g_str_equal(key, "Interface") == TRUE) {
303                         dbus_message_iter_get_basic(&value, &interface);
304
305                         DBG("Interface %s", interface);
306
307                         index = connman_inet_ifindex(interface);
308
309                         DBG("index %d", index);
310
311                         if (index < 0)
312                                 break;
313                 } else if (g_str_equal(key, "Address") == TRUE) {
314                         dbus_message_iter_get_basic(&value, &val);
315
316                         address = g_strdup(val);
317
318                         DBG("Address %s", address);
319                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
320                         nameservers = extract_nameservers(&value);
321
322                         DBG("Nameservers %s", nameservers);
323                 } else if (g_str_equal(key, "Gateway") == TRUE) {
324                         dbus_message_iter_get_basic(&value, &val);
325
326                         gateway = g_strdup(val);
327
328                         DBG("Gateway %s", gateway);
329                 }
330
331                 dbus_message_iter_next(&dict);
332         }
333
334         if (index < 0)
335                 goto out;
336
337         info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
338         if (info->address == NULL)
339                 goto out;
340
341         info->index = index;
342         connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
343
344         info->nameservers = nameservers;
345
346 out:
347         if (info->nameservers != nameservers)
348                 g_free(nameservers);
349
350         g_free(address);
351         g_free(gateway);
352 }
353
354 static gboolean device_changed(DBusConnection *connection,
355                                 DBusMessage *message,
356                                 void *user_data)
357 {
358         const char *path = dbus_message_get_path(message);
359         struct dundee_data *info = NULL;
360         DBusMessageIter iter, value;
361         const char *key;
362         const char *signature = DBUS_TYPE_STRING_AS_STRING
363                 DBUS_TYPE_VARIANT_AS_STRING;
364
365         if (dbus_message_has_signature(message, signature) == FALSE) {
366                 connman_error("dundee signature does not match");
367                 return TRUE;
368         }
369
370         info = g_hash_table_lookup(dundee_devices, path);
371         if (info == NULL)
372                 return TRUE;
373
374         if (dbus_message_iter_init(message, &iter) == FALSE)
375                 return TRUE;
376
377         dbus_message_iter_get_basic(&iter, &key);
378
379         dbus_message_iter_next(&iter);
380         dbus_message_iter_recurse(&iter, &value);
381
382         /*
383          * Dundee guarantees the ordering of Settings and
384          * Active. Settings will always be send before Active = True.
385          * That means we don't have to order here.
386          */
387         if (g_str_equal(key, "Active") == TRUE) {
388                 dbus_message_iter_get_basic(&value, &info->active);
389
390                 DBG("%s Active %d", info->path, info->active);
391         } else if (g_str_equal(key, "Settings") == TRUE) {
392                 DBG("%s Settings", info->path);
393
394                 extract_settings(&value, info);
395         } else if (g_str_equal(key, "Name") == TRUE) {
396                 char *name;
397
398                 dbus_message_iter_get_basic(&value, &name);
399
400                 g_free(info->name);
401                 info->name = g_strdup(name);
402
403                 DBG("%s Name %s", info->path, info->name);
404
405                 connman_network_set_name(info->network, info->name);
406                 connman_network_update(info->network);
407         }
408
409         return TRUE;
410 }
411
412 static void add_device(const char *path, DBusMessageIter *properties)
413 {
414         struct dundee_data *info;
415
416         info = g_hash_table_lookup(dundee_devices, path);
417         if (info != NULL)
418                 return;
419
420         info = g_try_new0(struct dundee_data, 1);
421         if (info == NULL)
422                 return;
423
424         info->path = g_strdup(path);
425
426         while (dbus_message_iter_get_arg_type(properties) ==
427                         DBUS_TYPE_DICT_ENTRY) {
428                 DBusMessageIter entry, value;
429                 const char *key;
430
431                 dbus_message_iter_recurse(properties, &entry);
432                 dbus_message_iter_get_basic(&entry, &key);
433
434                 dbus_message_iter_next(&entry);
435                 dbus_message_iter_recurse(&entry, &value);
436
437                 if (g_str_equal(key, "Active") == TRUE) {
438                         dbus_message_iter_get_basic(&value, &info->active);
439
440                         DBG("%s Active %d", info->path, info->active);
441                 } else if (g_str_equal(key, "Settings") == TRUE) {
442                         DBG("%s Settings", info->path);
443
444                         extract_settings(&value, info);
445                 } else if (g_str_equal(key, "Name") == TRUE) {
446                         char *name;
447
448                         dbus_message_iter_get_basic(&value, &name);
449
450                         info->name = g_strdup(name);
451
452                         DBG("%s Name %s", info->path, info->name);
453                 }
454
455                 dbus_message_iter_next(properties);
456         }
457
458         g_hash_table_insert(dundee_devices, g_strdup(path), info);
459
460         create_device(info);
461         create_network(info);
462 }
463
464 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
465                                 void *user_data)
466 {
467         DBusMessageIter iter, properties;
468         const char *path;
469         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
470                 DBUS_TYPE_ARRAY_AS_STRING
471                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
472                 DBUS_TYPE_STRING_AS_STRING
473                 DBUS_TYPE_VARIANT_AS_STRING
474                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
475
476         if (dbus_message_has_signature(message, signature) == FALSE) {
477                 connman_error("dundee signature does not match");
478                 return TRUE;
479         }
480
481         DBG("");
482
483         if (dbus_message_iter_init(message, &iter) == FALSE)
484                 return TRUE;
485
486         dbus_message_iter_get_basic(&iter, &path);
487
488         dbus_message_iter_next(&iter);
489         dbus_message_iter_recurse(&iter, &properties);
490
491         add_device(path, &properties);
492
493         return TRUE;
494 }
495
496 static void remove_device(DBusConnection *connection, const char *path)
497 {
498         DBG("path %s", path);
499
500         g_hash_table_remove(dundee_devices, path);
501 }
502
503 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
504                                 void *user_data)
505 {
506         const char *path;
507         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
508
509         if (dbus_message_has_signature(message, signature) == FALSE) {
510                 connman_error("dundee signature does not match");
511                 return TRUE;
512         }
513
514         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
515                                 DBUS_TYPE_INVALID);
516         remove_device(connection, path);
517         return TRUE;
518 }
519
520 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
521 {
522         DBusMessage *reply;
523         DBusError error;
524         DBusMessageIter array, dict;
525         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
526                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
527                 DBUS_TYPE_OBJECT_PATH_AS_STRING
528                 DBUS_TYPE_ARRAY_AS_STRING
529                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
530                 DBUS_TYPE_STRING_AS_STRING
531                 DBUS_TYPE_VARIANT_AS_STRING
532                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
533                 DBUS_STRUCT_END_CHAR_AS_STRING;
534
535         DBG("");
536
537         reply = dbus_pending_call_steal_reply(call);
538
539         if (dbus_message_has_signature(reply, signature) == FALSE) {
540                 connman_error("dundee signature does not match");
541                 goto done;
542         }
543
544         dbus_error_init(&error);
545
546         if (dbus_set_error_from_message(&error, reply) == TRUE) {
547                 connman_error("%s", error.message);
548                 dbus_error_free(&error);
549                 goto done;
550         }
551
552         if (dbus_message_iter_init(reply, &array) == FALSE)
553                 goto done;
554
555         dbus_message_iter_recurse(&array, &dict);
556
557         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
558                 DBusMessageIter value, properties;
559                 const char *path;
560
561                 dbus_message_iter_recurse(&dict, &value);
562                 dbus_message_iter_get_basic(&value, &path);
563
564                 dbus_message_iter_next(&value);
565                 dbus_message_iter_recurse(&value, &properties);
566
567                 add_device(path, &properties);
568
569                 dbus_message_iter_next(&dict);
570         }
571
572 done:
573         dbus_message_unref(reply);
574
575         dbus_pending_call_unref(call);
576 }
577
578 static int manager_get_devices(void)
579 {
580         DBusMessage *message;
581         DBusPendingCall *call;
582
583         DBG("");
584
585         message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
586                                         DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
587         if (message == NULL)
588                 return -ENOMEM;
589
590         if (dbus_connection_send_with_reply(connection, message,
591                                                 &call, TIMEOUT) == FALSE) {
592                 connman_error("Failed to call GetDevices()");
593                 dbus_message_unref(message);
594                 return -EINVAL;
595         }
596
597         if (call == NULL) {
598                 connman_error("D-Bus connection not available");
599                 dbus_message_unref(message);
600                 return -EINVAL;
601         }
602
603         dbus_pending_call_set_notify(call, manager_get_devices_reply,
604                                         NULL, NULL);
605
606         dbus_message_unref(message);
607
608         return -EINPROGRESS;
609 }
610
611 static void dundee_connect(DBusConnection *connection, void *user_data)
612 {
613         DBG("connection %p", connection);
614
615         dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
616                                         g_free, device_destroy);
617
618         manager_get_devices();
619 }
620
621 static void dundee_disconnect(DBusConnection *connection, void *user_data)
622 {
623         DBG("connection %p", connection);
624
625         g_hash_table_destroy(dundee_devices);
626         dundee_devices = NULL;
627 }
628
629 static guint watch;
630 static guint added_watch;
631 static guint removed_watch;
632 static guint device_watch;
633
634 static int dundee_init(void)
635 {
636         int err;
637
638         connection = connman_dbus_get_connection();
639         if (connection == NULL)
640                 return -EIO;
641
642         watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
643                         dundee_connect, dundee_disconnect, NULL, NULL);
644
645         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
646                                                 DUNDEE_MANAGER_INTERFACE,
647                                                 DEVICE_ADDED, device_added,
648                                                 NULL, NULL);
649
650         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
651                                                 DUNDEE_MANAGER_INTERFACE,
652                                                 DEVICE_REMOVED, device_removed,
653                                                 NULL, NULL);
654
655         device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
656                                                 DUNDEE_DEVICE_INTERFACE,
657                                                 PROPERTY_CHANGED,
658                                                 device_changed,
659                                                 NULL, NULL);
660
661
662         if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
663                         device_watch == 0) {
664                 err = -EIO;
665                 goto remove;
666         }
667
668         err = connman_network_driver_register(&network_driver);
669         if (err < 0)
670                 goto remove;
671
672         err = connman_device_driver_register(&dundee_driver);
673         if (err < 0) {
674                 connman_network_driver_unregister(&network_driver);
675                 goto remove;
676         }
677
678         return 0;
679
680 remove:
681         g_dbus_remove_watch(connection, watch);
682         g_dbus_remove_watch(connection, added_watch);
683         g_dbus_remove_watch(connection, removed_watch);
684         g_dbus_remove_watch(connection, device_watch);
685
686         dbus_connection_unref(connection);
687
688         return err;
689 }
690
691 static void dundee_exit(void)
692 {
693         g_dbus_remove_watch(connection, watch);
694         g_dbus_remove_watch(connection, added_watch);
695         g_dbus_remove_watch(connection, removed_watch);
696         g_dbus_remove_watch(connection, device_watch);
697
698         connman_device_driver_unregister(&dundee_driver);
699         connman_network_driver_unregister(&network_driver);
700
701         dbus_connection_unref(connection);
702 }
703
704 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
705                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)