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