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