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