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