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