dundee: Parse device properties in add_device
[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 <errno.h>
27
28 #include <gdbus.h>
29
30 #define CONNMAN_API_SUBJECT_TO_CHANGE
31 #include <connman/plugin.h>
32 #include <connman/device.h>
33 #include <connman/network.h>
34 #include <connman/inet.h>
35 #include <connman/dbus.h>
36
37 #define DUNDEE_SERVICE                  "org.ofono.dundee"
38 #define DUNDEE_MANAGER_INTERFACE        DUNDEE_SERVICE ".Manager"
39
40 #define DEVICE_ADDED                    "DeviceAdded"
41 #define DEVICE_REMOVED                  "DeviceRemoved"
42
43 #define GET_DEVICES                     "GetDevices"
44
45 #define TIMEOUT 40000
46
47 static DBusConnection *connection;
48
49 static GHashTable *dundee_devices = NULL;
50
51 struct dundee_data {
52         char *path;
53         char *name;
54
55         connman_bool_t active;
56
57         int index;
58
59         /* IPv4 Settings */
60         enum connman_ipconfig_method method;
61         struct connman_ipaddress *address;
62         char *nameservers;
63 };
64
65 static void device_destroy(gpointer data)
66 {
67         struct dundee_data *info = data;
68
69         g_free(info->path);
70         g_free(info->name);
71
72         g_free(info);
73 }
74
75 static int network_probe(struct connman_network *network)
76 {
77         DBG("network %p", network);
78
79         return 0;
80 }
81
82 static void network_remove(struct connman_network *network)
83 {
84         DBG("network %p", network);
85 }
86
87 static int network_connect(struct connman_network *network)
88 {
89         DBG("network %p", network);
90
91         return 0;
92 }
93
94 static int network_disconnect(struct connman_network *network)
95 {
96         DBG("network %p", network);
97
98         return 0;
99 }
100
101 static struct connman_network_driver network_driver = {
102         .name           = "network",
103         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
104         .probe          = network_probe,
105         .remove         = network_remove,
106         .connect        = network_connect,
107         .disconnect     = network_disconnect,
108 };
109
110 static int dundee_probe(struct connman_device *device)
111 {
112         DBG("device %p", device);
113
114         return 0;
115 }
116
117 static void dundee_remove(struct connman_device *device)
118 {
119         DBG("device %p", device);
120 }
121
122 static int dundee_enable(struct connman_device *device)
123 {
124         DBG("device %p", device);
125
126         return 0;
127 }
128
129 static int dundee_disable(struct connman_device *device)
130 {
131         DBG("device %p", device);
132
133         return 0;
134 }
135
136 static struct connman_device_driver dundee_driver = {
137         .name           = "dundee",
138         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
139         .probe          = dundee_probe,
140         .remove         = dundee_remove,
141         .enable         = dundee_enable,
142         .disable        = dundee_disable,
143 };
144
145 static char *extract_nameservers(DBusMessageIter *array)
146 {
147         DBusMessageIter entry;
148         char *nameservers = NULL;
149         char *tmp;
150
151         dbus_message_iter_recurse(array, &entry);
152
153         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
154                 const char *nameserver;
155
156                 dbus_message_iter_get_basic(&entry, &nameserver);
157
158                 if (nameservers == NULL) {
159                         nameservers = g_strdup(nameserver);
160                 } else {
161                         tmp = nameservers;
162                         nameservers = g_strdup_printf("%s %s", tmp, nameserver);
163                         g_free(tmp);
164                 }
165
166                 dbus_message_iter_next(&entry);
167         }
168
169         return nameservers;
170 }
171
172 static void extract_settings(DBusMessageIter *array,
173                                 struct dundee_data *info)
174 {
175         DBusMessageIter dict;
176         char *address = NULL, *gateway = NULL;
177         char *nameservers = NULL;
178         const char *interface = NULL;
179         int index = -1;
180
181         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
182                 return;
183
184         dbus_message_iter_recurse(array, &dict);
185
186         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
187                 DBusMessageIter entry, value;
188                 const char *key, *val;
189
190                 dbus_message_iter_recurse(&dict, &entry);
191                 dbus_message_iter_get_basic(&entry, &key);
192
193                 dbus_message_iter_next(&entry);
194                 dbus_message_iter_recurse(&entry, &value);
195
196                 if (g_str_equal(key, "Interface") == TRUE) {
197                         dbus_message_iter_get_basic(&value, &interface);
198
199                         DBG("Interface %s", interface);
200
201                         index = connman_inet_ifindex(interface);
202
203                         DBG("index %d", index);
204
205                         if (index < 0)
206                                 break;
207                 } else if (g_str_equal(key, "Address") == TRUE) {
208                         dbus_message_iter_get_basic(&value, &val);
209
210                         address = g_strdup(val);
211
212                         DBG("Address %s", address);
213                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
214                         nameservers = extract_nameservers(&value);
215
216                         DBG("Nameservers %s", nameservers);
217                 } else if (g_str_equal(key, "Gateway") == TRUE) {
218                         dbus_message_iter_get_basic(&value, &val);
219
220                         gateway = g_strdup(val);
221
222                         DBG("Gateway %s", gateway);
223                 }
224
225                 dbus_message_iter_next(&dict);
226         }
227
228         if (index < 0)
229                 goto out;
230
231         info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
232         if (info->address == NULL)
233                 goto out;
234
235         info->index = index;
236         connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
237
238         info->nameservers = nameservers;
239
240 out:
241         if (info->nameservers != nameservers)
242                 g_free(nameservers);
243
244         g_free(address);
245         g_free(gateway);
246 }
247
248 static void add_device(const char *path, DBusMessageIter *properties)
249 {
250         struct dundee_data *info;
251
252         info = g_hash_table_lookup(dundee_devices, path);
253         if (info != NULL)
254                 return;
255
256         info = g_try_new0(struct dundee_data, 1);
257         if (info == NULL)
258                 return;
259
260         info->path = g_strdup(path);
261
262         while (dbus_message_iter_get_arg_type(properties) ==
263                         DBUS_TYPE_DICT_ENTRY) {
264                 DBusMessageIter entry, value;
265                 const char *key;
266
267                 dbus_message_iter_recurse(properties, &entry);
268                 dbus_message_iter_get_basic(&entry, &key);
269
270                 dbus_message_iter_next(&entry);
271                 dbus_message_iter_recurse(&entry, &value);
272
273                 if (g_str_equal(key, "Active") == TRUE) {
274                         dbus_message_iter_get_basic(&value, &info->active);
275
276                         DBG("%s Active %d", info->path, info->active);
277                 } else if (g_str_equal(key, "Settings") == TRUE) {
278                         DBG("%s Settings", info->path);
279
280                         extract_settings(&value, info);
281                 } else if (g_str_equal(key, "Name") == TRUE) {
282                         char *name;
283
284                         dbus_message_iter_get_basic(&value, &name);
285
286                         info->name = g_strdup(name);
287
288                         DBG("%s Name %s", info->path, info->name);
289                 }
290
291                 dbus_message_iter_next(properties);
292         }
293
294         g_hash_table_insert(dundee_devices, g_strdup(path), info);
295 }
296
297 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
298                                 void *user_data)
299 {
300         DBusMessageIter iter, properties;
301         const char *path;
302         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
303                 DBUS_TYPE_ARRAY_AS_STRING
304                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
305                 DBUS_TYPE_STRING_AS_STRING
306                 DBUS_TYPE_VARIANT_AS_STRING
307                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
308
309         if (dbus_message_has_signature(message, signature) == FALSE) {
310                 connman_error("dundee signature does not match");
311                 return TRUE;
312         }
313
314         DBG("");
315
316         if (dbus_message_iter_init(message, &iter) == FALSE)
317                 return TRUE;
318
319         dbus_message_iter_get_basic(&iter, &path);
320
321         dbus_message_iter_next(&iter);
322         dbus_message_iter_recurse(&iter, &properties);
323
324         add_device(path, &properties);
325
326         return TRUE;
327 }
328
329 static void remove_device(DBusConnection *connection, const char *path)
330 {
331         DBG("path %s", path);
332
333         g_hash_table_remove(dundee_devices, path);
334 }
335
336 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
337                                 void *user_data)
338 {
339         const char *path;
340         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
341
342         if (dbus_message_has_signature(message, signature) == FALSE) {
343                 connman_error("dundee signature does not match");
344                 return TRUE;
345         }
346
347         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
348                                 DBUS_TYPE_INVALID);
349         remove_device(connection, path);
350         return TRUE;
351 }
352
353 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
354 {
355         DBusMessage *reply;
356         DBusError error;
357         DBusMessageIter array, dict;
358         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
359                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
360                 DBUS_TYPE_OBJECT_PATH_AS_STRING
361                 DBUS_TYPE_ARRAY_AS_STRING
362                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
363                 DBUS_TYPE_STRING_AS_STRING
364                 DBUS_TYPE_VARIANT_AS_STRING
365                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
366                 DBUS_STRUCT_END_CHAR_AS_STRING;
367
368         DBG("");
369
370         reply = dbus_pending_call_steal_reply(call);
371
372         if (dbus_message_has_signature(reply, signature) == FALSE) {
373                 connman_error("dundee signature does not match");
374                 goto done;
375         }
376
377         dbus_error_init(&error);
378
379         if (dbus_set_error_from_message(&error, reply) == TRUE) {
380                 connman_error("%s", error.message);
381                 dbus_error_free(&error);
382                 goto done;
383         }
384
385         if (dbus_message_iter_init(reply, &array) == FALSE)
386                 goto done;
387
388         dbus_message_iter_recurse(&array, &dict);
389
390         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
391                 DBusMessageIter value, properties;
392                 const char *path;
393
394                 dbus_message_iter_recurse(&dict, &value);
395                 dbus_message_iter_get_basic(&value, &path);
396
397                 dbus_message_iter_next(&value);
398                 dbus_message_iter_recurse(&value, &properties);
399
400                 add_device(path, &properties);
401
402                 dbus_message_iter_next(&dict);
403         }
404
405 done:
406         dbus_message_unref(reply);
407
408         dbus_pending_call_unref(call);
409 }
410
411 static int manager_get_devices(void)
412 {
413         DBusMessage *message;
414         DBusPendingCall *call;
415
416         DBG("");
417
418         message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
419                                         DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
420         if (message == NULL)
421                 return -ENOMEM;
422
423         if (dbus_connection_send_with_reply(connection, message,
424                                                 &call, TIMEOUT) == FALSE) {
425                 connman_error("Failed to call GetDevices()");
426                 dbus_message_unref(message);
427                 return -EINVAL;
428         }
429
430         if (call == NULL) {
431                 connman_error("D-Bus connection not available");
432                 dbus_message_unref(message);
433                 return -EINVAL;
434         }
435
436         dbus_pending_call_set_notify(call, manager_get_devices_reply,
437                                         NULL, NULL);
438
439         dbus_message_unref(message);
440
441         return -EINPROGRESS;
442 }
443
444 static void dundee_connect(DBusConnection *connection, void *user_data)
445 {
446         DBG("connection %p", connection);
447
448         dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
449                                         g_free, device_destroy);
450
451         manager_get_devices();
452 }
453
454 static void dundee_disconnect(DBusConnection *connection, void *user_data)
455 {
456         DBG("connection %p", connection);
457
458         g_hash_table_destroy(dundee_devices);
459         dundee_devices = NULL;
460 }
461
462 static guint watch;
463 static guint added_watch;
464 static guint removed_watch;
465
466 static int dundee_init(void)
467 {
468         int err;
469
470         connection = connman_dbus_get_connection();
471         if (connection == NULL)
472                 return -EIO;
473
474         watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
475                         dundee_connect, dundee_disconnect, NULL, NULL);
476
477         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
478                                                 DUNDEE_MANAGER_INTERFACE,
479                                                 DEVICE_ADDED, device_added,
480                                                 NULL, NULL);
481
482         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
483                                                 DUNDEE_MANAGER_INTERFACE,
484                                                 DEVICE_REMOVED, device_removed,
485                                                 NULL, NULL);
486
487         if (watch == 0 || added_watch == 0 || removed_watch == 0) {
488                 err = -EIO;
489                 goto remove;
490         }
491
492         err = connman_network_driver_register(&network_driver);
493         if (err < 0)
494                 goto remove;
495
496         err = connman_device_driver_register(&dundee_driver);
497         if (err < 0) {
498                 connman_network_driver_unregister(&network_driver);
499                 goto remove;
500         }
501
502         return 0;
503
504 remove:
505         g_dbus_remove_watch(connection, watch);
506         g_dbus_remove_watch(connection, added_watch);
507         g_dbus_remove_watch(connection, removed_watch);
508
509         dbus_connection_unref(connection);
510
511         return err;
512 }
513
514 static void dundee_exit(void)
515 {
516         g_dbus_remove_watch(connection, watch);
517         g_dbus_remove_watch(connection, added_watch);
518         g_dbus_remove_watch(connection, removed_watch);
519
520         connman_device_driver_unregister(&dundee_driver);
521         connman_network_driver_unregister(&network_driver);
522
523         dbus_connection_unref(connection);
524 }
525
526 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
527                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)