dundee: Set device active/inactive
[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 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         DBG("device %p", device);
343
344         return 0;
345 }
346
347 static void dundee_remove(struct connman_device *device)
348 {
349         DBG("device %p", device);
350 }
351
352 static int dundee_enable(struct connman_device *device)
353 {
354         DBG("device %p", device);
355
356         return 0;
357 }
358
359 static int dundee_disable(struct connman_device *device)
360 {
361         DBG("device %p", device);
362
363         return 0;
364 }
365
366 static struct connman_device_driver dundee_driver = {
367         .name           = "dundee",
368         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
369         .probe          = dundee_probe,
370         .remove         = dundee_remove,
371         .enable         = dundee_enable,
372         .disable        = dundee_disable,
373 };
374
375 static char *extract_nameservers(DBusMessageIter *array)
376 {
377         DBusMessageIter entry;
378         char *nameservers = NULL;
379         char *tmp;
380
381         dbus_message_iter_recurse(array, &entry);
382
383         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
384                 const char *nameserver;
385
386                 dbus_message_iter_get_basic(&entry, &nameserver);
387
388                 if (nameservers == NULL) {
389                         nameservers = g_strdup(nameserver);
390                 } else {
391                         tmp = nameservers;
392                         nameservers = g_strdup_printf("%s %s", tmp, nameserver);
393                         g_free(tmp);
394                 }
395
396                 dbus_message_iter_next(&entry);
397         }
398
399         return nameservers;
400 }
401
402 static void extract_settings(DBusMessageIter *array,
403                                 struct dundee_data *info)
404 {
405         DBusMessageIter dict;
406         char *address = NULL, *gateway = NULL;
407         char *nameservers = NULL;
408         const char *interface = NULL;
409         int index = -1;
410
411         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
412                 return;
413
414         dbus_message_iter_recurse(array, &dict);
415
416         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
417                 DBusMessageIter entry, value;
418                 const char *key, *val;
419
420                 dbus_message_iter_recurse(&dict, &entry);
421                 dbus_message_iter_get_basic(&entry, &key);
422
423                 dbus_message_iter_next(&entry);
424                 dbus_message_iter_recurse(&entry, &value);
425
426                 if (g_str_equal(key, "Interface") == TRUE) {
427                         dbus_message_iter_get_basic(&value, &interface);
428
429                         DBG("Interface %s", interface);
430
431                         index = connman_inet_ifindex(interface);
432
433                         DBG("index %d", index);
434
435                         if (index < 0)
436                                 break;
437                 } else if (g_str_equal(key, "Address") == TRUE) {
438                         dbus_message_iter_get_basic(&value, &val);
439
440                         address = g_strdup(val);
441
442                         DBG("Address %s", address);
443                 } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
444                         nameservers = extract_nameservers(&value);
445
446                         DBG("Nameservers %s", nameservers);
447                 } else if (g_str_equal(key, "Gateway") == TRUE) {
448                         dbus_message_iter_get_basic(&value, &val);
449
450                         gateway = g_strdup(val);
451
452                         DBG("Gateway %s", gateway);
453                 }
454
455                 dbus_message_iter_next(&dict);
456         }
457
458         if (index < 0)
459                 goto out;
460
461         info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
462         if (info->address == NULL)
463                 goto out;
464
465         info->index = index;
466         connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
467
468         info->nameservers = nameservers;
469
470 out:
471         if (info->nameservers != nameservers)
472                 g_free(nameservers);
473
474         g_free(address);
475         g_free(gateway);
476 }
477
478 static gboolean device_changed(DBusConnection *connection,
479                                 DBusMessage *message,
480                                 void *user_data)
481 {
482         const char *path = dbus_message_get_path(message);
483         struct dundee_data *info = NULL;
484         DBusMessageIter iter, value;
485         const char *key;
486         const char *signature = DBUS_TYPE_STRING_AS_STRING
487                 DBUS_TYPE_VARIANT_AS_STRING;
488
489         if (dbus_message_has_signature(message, signature) == FALSE) {
490                 connman_error("dundee signature does not match");
491                 return TRUE;
492         }
493
494         info = g_hash_table_lookup(dundee_devices, path);
495         if (info == NULL)
496                 return TRUE;
497
498         if (dbus_message_iter_init(message, &iter) == FALSE)
499                 return TRUE;
500
501         dbus_message_iter_get_basic(&iter, &key);
502
503         dbus_message_iter_next(&iter);
504         dbus_message_iter_recurse(&iter, &value);
505
506         /*
507          * Dundee guarantees the ordering of Settings and
508          * Active. Settings will always be send before Active = True.
509          * That means we don't have to order here.
510          */
511         if (g_str_equal(key, "Active") == TRUE) {
512                 dbus_message_iter_get_basic(&value, &info->active);
513
514                 DBG("%s Active %d", info->path, info->active);
515
516                 if (info->active == TRUE)
517                         set_connected(info);
518                 else
519                         set_disconnected(info);
520         } else if (g_str_equal(key, "Settings") == TRUE) {
521                 DBG("%s Settings", info->path);
522
523                 extract_settings(&value, info);
524         } else if (g_str_equal(key, "Name") == TRUE) {
525                 char *name;
526
527                 dbus_message_iter_get_basic(&value, &name);
528
529                 g_free(info->name);
530                 info->name = g_strdup(name);
531
532                 DBG("%s Name %s", info->path, info->name);
533
534                 connman_network_set_name(info->network, info->name);
535                 connman_network_update(info->network);
536         }
537
538         return TRUE;
539 }
540
541 static void add_device(const char *path, DBusMessageIter *properties)
542 {
543         struct dundee_data *info;
544
545         info = g_hash_table_lookup(dundee_devices, path);
546         if (info != NULL)
547                 return;
548
549         info = g_try_new0(struct dundee_data, 1);
550         if (info == NULL)
551                 return;
552
553         info->path = g_strdup(path);
554
555         while (dbus_message_iter_get_arg_type(properties) ==
556                         DBUS_TYPE_DICT_ENTRY) {
557                 DBusMessageIter entry, value;
558                 const char *key;
559
560                 dbus_message_iter_recurse(properties, &entry);
561                 dbus_message_iter_get_basic(&entry, &key);
562
563                 dbus_message_iter_next(&entry);
564                 dbus_message_iter_recurse(&entry, &value);
565
566                 if (g_str_equal(key, "Active") == TRUE) {
567                         dbus_message_iter_get_basic(&value, &info->active);
568
569                         DBG("%s Active %d", info->path, info->active);
570                 } else if (g_str_equal(key, "Settings") == TRUE) {
571                         DBG("%s Settings", info->path);
572
573                         extract_settings(&value, info);
574                 } else if (g_str_equal(key, "Name") == TRUE) {
575                         char *name;
576
577                         dbus_message_iter_get_basic(&value, &name);
578
579                         info->name = g_strdup(name);
580
581                         DBG("%s Name %s", info->path, info->name);
582                 }
583
584                 dbus_message_iter_next(properties);
585         }
586
587         g_hash_table_insert(dundee_devices, g_strdup(path), info);
588
589         create_device(info);
590         create_network(info);
591
592         if (info->active == TRUE)
593                 set_connected(info);
594 }
595
596 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
597                                 void *user_data)
598 {
599         DBusMessageIter iter, properties;
600         const char *path;
601         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
602                 DBUS_TYPE_ARRAY_AS_STRING
603                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
604                 DBUS_TYPE_STRING_AS_STRING
605                 DBUS_TYPE_VARIANT_AS_STRING
606                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
607
608         if (dbus_message_has_signature(message, signature) == FALSE) {
609                 connman_error("dundee signature does not match");
610                 return TRUE;
611         }
612
613         DBG("");
614
615         if (dbus_message_iter_init(message, &iter) == FALSE)
616                 return TRUE;
617
618         dbus_message_iter_get_basic(&iter, &path);
619
620         dbus_message_iter_next(&iter);
621         dbus_message_iter_recurse(&iter, &properties);
622
623         add_device(path, &properties);
624
625         return TRUE;
626 }
627
628 static void remove_device(DBusConnection *connection, const char *path)
629 {
630         DBG("path %s", path);
631
632         g_hash_table_remove(dundee_devices, path);
633 }
634
635 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
636                                 void *user_data)
637 {
638         const char *path;
639         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
640
641         if (dbus_message_has_signature(message, signature) == FALSE) {
642                 connman_error("dundee signature does not match");
643                 return TRUE;
644         }
645
646         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
647                                 DBUS_TYPE_INVALID);
648         remove_device(connection, path);
649         return TRUE;
650 }
651
652 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
653 {
654         DBusMessage *reply;
655         DBusError error;
656         DBusMessageIter array, dict;
657         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
658                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
659                 DBUS_TYPE_OBJECT_PATH_AS_STRING
660                 DBUS_TYPE_ARRAY_AS_STRING
661                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
662                 DBUS_TYPE_STRING_AS_STRING
663                 DBUS_TYPE_VARIANT_AS_STRING
664                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
665                 DBUS_STRUCT_END_CHAR_AS_STRING;
666
667         DBG("");
668
669         reply = dbus_pending_call_steal_reply(call);
670
671         if (dbus_message_has_signature(reply, signature) == FALSE) {
672                 connman_error("dundee signature does not match");
673                 goto done;
674         }
675
676         dbus_error_init(&error);
677
678         if (dbus_set_error_from_message(&error, reply) == TRUE) {
679                 connman_error("%s", error.message);
680                 dbus_error_free(&error);
681                 goto done;
682         }
683
684         if (dbus_message_iter_init(reply, &array) == FALSE)
685                 goto done;
686
687         dbus_message_iter_recurse(&array, &dict);
688
689         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
690                 DBusMessageIter value, properties;
691                 const char *path;
692
693                 dbus_message_iter_recurse(&dict, &value);
694                 dbus_message_iter_get_basic(&value, &path);
695
696                 dbus_message_iter_next(&value);
697                 dbus_message_iter_recurse(&value, &properties);
698
699                 add_device(path, &properties);
700
701                 dbus_message_iter_next(&dict);
702         }
703
704 done:
705         dbus_message_unref(reply);
706
707         dbus_pending_call_unref(call);
708 }
709
710 static int manager_get_devices(void)
711 {
712         DBusMessage *message;
713         DBusPendingCall *call;
714
715         DBG("");
716
717         message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
718                                         DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
719         if (message == NULL)
720                 return -ENOMEM;
721
722         if (dbus_connection_send_with_reply(connection, message,
723                                                 &call, TIMEOUT) == FALSE) {
724                 connman_error("Failed to call GetDevices()");
725                 dbus_message_unref(message);
726                 return -EINVAL;
727         }
728
729         if (call == NULL) {
730                 connman_error("D-Bus connection not available");
731                 dbus_message_unref(message);
732                 return -EINVAL;
733         }
734
735         dbus_pending_call_set_notify(call, manager_get_devices_reply,
736                                         NULL, NULL);
737
738         dbus_message_unref(message);
739
740         return -EINPROGRESS;
741 }
742
743 static void dundee_connect(DBusConnection *connection, void *user_data)
744 {
745         DBG("connection %p", connection);
746
747         dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
748                                         g_free, device_destroy);
749
750         manager_get_devices();
751 }
752
753 static void dundee_disconnect(DBusConnection *connection, void *user_data)
754 {
755         DBG("connection %p", connection);
756
757         g_hash_table_destroy(dundee_devices);
758         dundee_devices = NULL;
759 }
760
761 static guint watch;
762 static guint added_watch;
763 static guint removed_watch;
764 static guint device_watch;
765
766 static int dundee_init(void)
767 {
768         int err;
769
770         connection = connman_dbus_get_connection();
771         if (connection == NULL)
772                 return -EIO;
773
774         watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
775                         dundee_connect, dundee_disconnect, NULL, NULL);
776
777         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
778                                                 DUNDEE_MANAGER_INTERFACE,
779                                                 DEVICE_ADDED, device_added,
780                                                 NULL, NULL);
781
782         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
783                                                 DUNDEE_MANAGER_INTERFACE,
784                                                 DEVICE_REMOVED, device_removed,
785                                                 NULL, NULL);
786
787         device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
788                                                 DUNDEE_DEVICE_INTERFACE,
789                                                 PROPERTY_CHANGED,
790                                                 device_changed,
791                                                 NULL, NULL);
792
793
794         if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
795                         device_watch == 0) {
796                 err = -EIO;
797                 goto remove;
798         }
799
800         err = connman_network_driver_register(&network_driver);
801         if (err < 0)
802                 goto remove;
803
804         err = connman_device_driver_register(&dundee_driver);
805         if (err < 0) {
806                 connman_network_driver_unregister(&network_driver);
807                 goto remove;
808         }
809
810         return 0;
811
812 remove:
813         g_dbus_remove_watch(connection, watch);
814         g_dbus_remove_watch(connection, added_watch);
815         g_dbus_remove_watch(connection, removed_watch);
816         g_dbus_remove_watch(connection, device_watch);
817
818         dbus_connection_unref(connection);
819
820         return err;
821 }
822
823 static void dundee_exit(void)
824 {
825         g_dbus_remove_watch(connection, watch);
826         g_dbus_remove_watch(connection, added_watch);
827         g_dbus_remove_watch(connection, removed_watch);
828         g_dbus_remove_watch(connection, device_watch);
829
830         connman_device_driver_unregister(&dundee_driver);
831         connman_network_driver_unregister(&network_driver);
832
833         dbus_connection_unref(connection);
834 }
835
836 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
837                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)