provider: Implement IPv6 route setting
[platform/upstream/connman.git] / src / provider.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. 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 <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection = NULL;
34
35 static GHashTable *provider_hash = NULL;
36
37 static GSList *driver_list = NULL;
38
39 struct connman_route {
40         int family;
41         char *host;
42         char *netmask;
43         char *gateway;
44 };
45
46 struct connman_provider {
47         struct connman_element element;
48         struct connman_service *vpn_service;
49         char *identifier;
50         char *name;
51         char *type;
52         char *host;
53         char *dns;
54         char *domain;
55         GSList *route_list;
56         struct connman_provider_driver *driver;
57         void *driver_data;
58 };
59
60 void __connman_provider_append_properties(struct connman_provider *provider,
61                                                         DBusMessageIter *iter)
62 {
63         if (provider->host != NULL)
64                 connman_dbus_dict_append_basic(iter, "Host",
65                                         DBUS_TYPE_STRING, &provider->host);
66
67         if (provider->domain != NULL)
68                 connman_dbus_dict_append_basic(iter, "Domain",
69                                         DBUS_TYPE_STRING, &provider->domain);
70
71         if (provider->type != NULL)
72                 connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
73                                                  &provider->type);
74 }
75
76 static struct connman_provider *connman_provider_lookup(const char *identifier)
77 {
78         struct connman_provider *provider = NULL;
79
80         provider = g_hash_table_lookup(provider_hash, identifier);
81
82         return provider;
83 }
84
85 static int connman_provider_setup_vpn_ipv4(struct connman_provider *provider,
86                                                 struct connman_element *element)
87 {
88         if (element == NULL || provider == NULL)
89                 return -EINVAL;
90
91         DBG("set vpn type %d", element->type);
92
93         g_free(element->ipv4.address);
94         element->ipv4.address = g_strdup(provider->element.ipv4.address);
95
96         g_free(element->ipv4.peer);
97         element->ipv4.peer = g_strdup(provider->element.ipv4.peer);
98
99         g_free(element->ipv4.netmask);
100         element->ipv4.netmask = g_strdup(provider->element.ipv4.netmask);
101
102         g_free(element->ipv4.gateway);
103         element->ipv4.gateway = g_strdup(provider->element.ipv4.gateway);
104
105         g_free(element->ipv4.broadcast);
106         element->ipv4.broadcast = g_strdup(provider->element.ipv4.broadcast);
107
108         g_free(element->ipv4.pac);
109         element->ipv4.pac = g_strdup(provider->element.ipv4.pac);
110
111         return connman_element_register(element, &provider->element);
112 }
113
114 struct connman_provider *connman_provider_ref(struct connman_provider *provider)
115 {
116         DBG("provider %p", provider);
117
118         if (connman_element_ref(&provider->element) == NULL)
119                 return NULL;
120
121         return provider;
122 }
123
124 void connman_provider_unref(struct connman_provider *provider)
125 {
126         DBG("provider %p", provider);
127
128         connman_element_unref(&provider->element);
129 }
130
131 static gboolean match_driver(struct connman_provider *provider,
132                                 struct connman_provider_driver *driver)
133 {
134         if (g_strcmp0(driver->name, provider->type) == 0)
135                 return TRUE;
136
137         return FALSE;
138 }
139
140 static int provider_probe(struct connman_provider *provider)
141 {
142         GSList *list;
143
144         DBG("provider %p name %s", provider, provider->name);
145
146         if (provider->driver != NULL)
147                 return -EALREADY;
148
149         for (list = driver_list; list; list = list->next) {
150                 struct connman_provider_driver *driver = list->data;
151
152                 if (match_driver(provider, driver) == FALSE)
153                         continue;
154
155                 DBG("driver %p name %s", driver, driver->name);
156
157                 if (driver->probe != NULL && driver->probe(provider) == 0) {
158                         provider->driver = driver;
159                         break;
160                 }
161         }
162
163         if (provider->driver == NULL)
164                 return -ENODEV;
165
166         return 0;
167 }
168
169 int __connman_provider_disconnect(struct connman_provider *provider)
170 {
171         int err;
172
173         DBG("provider %p", provider);
174
175         if (provider->driver != NULL && provider->driver->disconnect != NULL)
176                 err = provider->driver->disconnect(provider);
177         else
178                 return -EOPNOTSUPP;
179
180         if (provider->vpn_service != NULL)
181                 __connman_service_indicate_state(provider->vpn_service,
182                                         CONNMAN_SERVICE_STATE_DISCONNECT);
183         if (err < 0) {
184                 if (err != -EINPROGRESS)
185                         return err;
186
187                 return -EINPROGRESS;
188         }
189
190         return 0;
191 }
192
193 int __connman_provider_connect(struct connman_provider *provider)
194 {
195         int err;
196
197         DBG("provider %p", provider);
198
199         g_free(provider->element.ipv4.address);
200         g_free(provider->element.ipv4.peer);
201         g_free(provider->element.ipv4.netmask);
202         g_free(provider->element.ipv4.gateway);
203         g_free(provider->element.ipv4.broadcast);
204         g_free(provider->element.ipv4.pac);
205
206         provider->element.ipv4.address = NULL;
207         provider->element.ipv4.peer = NULL;
208         provider->element.ipv4.netmask = NULL;
209         provider->element.ipv4.gateway = NULL;
210         provider->element.ipv4.broadcast = NULL;
211         provider->element.ipv4.pac = NULL;
212
213         if (provider->driver != NULL && provider->driver->connect != NULL)
214                 err = provider->driver->connect(provider);
215         else
216                 return -EOPNOTSUPP;
217
218         if (err < 0) {
219                 if (err != -EINPROGRESS)
220                         return err;
221
222                 __connman_service_indicate_state(provider->vpn_service,
223                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
224                 return -EINPROGRESS;
225         }
226
227         return 0;
228 }
229
230 int __connman_provider_remove(const char *path)
231 {
232         struct connman_provider *provider;
233         GHashTableIter iter;
234         gpointer value, key;
235
236         DBG("path %s", path);
237
238         g_hash_table_iter_init(&iter, provider_hash);
239         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
240                 const char *srv_path;
241                 provider = value;
242
243                 if (provider->vpn_service == NULL)
244                         continue;
245
246                 srv_path = __connman_service_get_path(provider->vpn_service);
247
248                 if (g_strcmp0(srv_path, path) == 0) {
249                         DBG("Removing VPN %s", provider->identifier);
250                         g_hash_table_remove(provider_hash,
251                                                 provider->identifier);
252                         return 0;
253                 }
254         }
255
256         return -ENXIO;
257 }
258
259 static void provider_set_nameservers(struct connman_provider *provider)
260 {
261         char *nameservers = NULL, *name = NULL;
262         const char *value;
263         char *second_ns;
264
265         if (provider->dns == NULL)
266                 return;
267
268         name = connman_inet_ifname(provider->element.index);
269
270         nameservers = g_strdup(provider->dns);
271         value = nameservers;
272         second_ns = strchr(value, ' ');
273         if (second_ns)
274                 *(second_ns++) = 0;
275         __connman_service_append_nameserver(service, value);
276         value = second_ns;
277
278         while (value) {
279                 char *next = strchr(value, ' ');
280                 if (next)
281                         *(next++) = 0;
282
283                 connman_resolver_append(name, provider->domain, value);
284                 value = next;
285         }
286
287         g_free(nameservers);
288         g_free(name);
289 }
290
291 static int set_connected(struct connman_provider *provider,
292                                         connman_bool_t connected)
293 {
294         struct connman_service *service = provider->vpn_service;
295
296         if (service == NULL)
297                 return -ENODEV;
298
299         if (connected == TRUE) {
300                 enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
301                 struct connman_element *element;
302                 GSList *list;
303                 int err;
304
305                 __connman_service_indicate_state(provider->vpn_service,
306                                         CONNMAN_SERVICE_STATE_CONFIGURATION);
307
308                 type = CONNMAN_ELEMENT_TYPE_IPV4;
309
310                 element = connman_element_create(NULL);
311                 if (element == NULL)
312                         return -ENOMEM;
313
314                 element->type  = type;
315                 element->index = provider->element.index;
316
317                 err = connman_provider_setup_vpn_ipv4(provider, element);
318                 if (err < 0) {
319                         connman_element_unref(element);
320
321                         __connman_service_indicate_state(service,
322                                                 CONNMAN_SERVICE_STATE_FAILURE);
323
324                         return err;
325                 }
326
327                 __connman_service_indicate_state(service,
328                                                 CONNMAN_SERVICE_STATE_READY);
329
330                 __connman_service_set_domainname(service, provider->domain);
331
332                 provider_set_nameservers(provider);
333
334                 for (list = provider->route_list; list; list = list->next) {
335                         struct connman_route *route = list->data;
336                         int index = provider->element.index;
337
338                         if (route->family == AF_INET6) {
339                                 unsigned char prefix_len = atoi(route->netmask);
340
341                                 connman_inet_add_ipv6_network_route(index,
342                                                                 route->host,
343                                                                 route->gateway,
344                                                                 prefix_len);
345                         } else {
346                                 connman_inet_add_network_route(index,
347                                                         route->host,
348                                                         route->gateway,
349                                                         route->netmask);
350                         }
351                 }
352         } else {
353                 connman_element_unregister_children(&provider->element);
354                 __connman_service_indicate_state(service,
355                                                 CONNMAN_SERVICE_STATE_IDLE);
356         }
357
358         return 0;
359 }
360
361 int connman_provider_set_state(struct connman_provider *provider,
362                                         enum connman_provider_state state)
363 {
364         if (provider == NULL || provider->vpn_service == NULL)
365                 return -EINVAL;
366
367         switch (state) {
368         case CONNMAN_PROVIDER_STATE_UNKNOWN:
369                 return -EINVAL;
370         case CONNMAN_PROVIDER_STATE_IDLE:
371                 return set_connected(provider, FALSE);
372         case CONNMAN_PROVIDER_STATE_CONNECT:
373                 return __connman_service_indicate_state(provider->vpn_service,
374                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
375         case CONNMAN_PROVIDER_STATE_READY:
376                 return set_connected(provider, TRUE);
377         case CONNMAN_PROVIDER_STATE_DISCONNECT:
378                 return __connman_service_indicate_state(provider->vpn_service,
379                                         CONNMAN_SERVICE_STATE_DISCONNECT);
380         case CONNMAN_PROVIDER_STATE_FAILURE:
381                 return __connman_service_indicate_state(provider->vpn_service,
382                                         CONNMAN_SERVICE_STATE_FAILURE);
383         }
384
385         return -EINVAL;
386 }
387
388 static void unregister_provider(gpointer data)
389 {
390         struct connman_provider *provider = data;
391         struct connman_service *service = provider->vpn_service;
392
393         DBG("provider %p", provider);
394
395         provider->vpn_service = NULL;
396         __connman_service_put(service);
397
398         connman_element_unregister(&provider->element);
399         connman_provider_unref(provider);
400 }
401
402 static void provider_destruct(struct connman_element *element)
403 {
404         struct connman_provider *provider = element->private;
405         GSList *list;
406
407         DBG("provider %p", provider);
408
409         g_free(provider->name);
410         g_free(provider->type);
411         g_free(provider->domain);
412         g_free(provider->identifier);
413         g_free(provider->dns);
414
415         for (list = provider->route_list; list; list = list->next) {
416                 struct connman_route *route = list->data;
417
418                 g_free(route->host);
419                 g_free(route->netmask);
420                 g_free(route->gateway);
421         }
422         g_slist_free(provider->route_list);
423 }
424
425 static void provider_initialize(struct connman_provider *provider)
426 {
427         DBG("provider %p", provider);
428
429         __connman_element_initialize(&provider->element);
430
431         provider->element.private = provider;
432         provider->element.destruct = provider_destruct;
433
434         provider->element.ipv4.address = NULL;
435         provider->element.ipv4.netmask = NULL;
436         provider->element.ipv4.gateway = NULL;
437         provider->element.ipv4.broadcast = NULL;
438         provider->element.ipv4.pac = NULL;
439
440         provider->name = NULL;
441         provider->type = NULL;
442         provider->dns = NULL;
443         provider->domain = NULL;
444         provider->identifier = NULL;
445         provider->route_list = NULL;
446 }
447
448 static struct connman_provider *connman_provider_new(void)
449 {
450         struct connman_provider *provider;
451
452         provider = g_try_new0(struct connman_provider, 1);
453         if (provider == NULL)
454                 return NULL;
455
456         DBG("provider %p", provider);
457         provider_initialize(provider);
458
459         return provider;
460 }
461
462 static struct connman_provider *connman_provider_get(const char *identifier)
463 {
464         struct connman_provider *provider;
465
466         provider = g_hash_table_lookup(provider_hash, identifier);
467         if (provider != NULL)
468                 return provider;
469
470         provider = connman_provider_new();
471         if (provider == NULL)
472                 return NULL;
473
474         DBG("provider %p", provider);
475
476         provider->identifier = g_strdup(identifier);
477
478         g_hash_table_insert(provider_hash, provider->identifier, provider);
479
480         provider->element.name = g_strdup(identifier);
481         connman_element_register(&provider->element, NULL);
482
483         return provider;
484 }
485
486 static void provider_dbus_ident(char *ident)
487 {
488         int i, len = strlen(ident);
489
490         for (i = 0; i < len; i++) {
491                 if (ident[i] >= '0' && ident[i] <= '9')
492                         continue;
493                 if (ident[i] >= 'a' && ident[i] <= 'z')
494                         continue;
495                 if (ident[i] >= 'A' && ident[i] <= 'Z')
496                         continue;
497                 ident[i] = '_';
498         }
499 }
500
501 int __connman_provider_create_and_connect(DBusMessage *msg)
502 {
503         struct connman_provider *provider;
504         DBusMessageIter iter, array;
505         const char *type = NULL, *name = NULL, *service_path = NULL;
506         const char *host = NULL, *domain = NULL;
507         char *ident;
508         gboolean created = FALSE;
509         int err;
510
511         dbus_message_iter_init(msg, &iter);
512         dbus_message_iter_recurse(&iter, &array);
513
514         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
515                 DBusMessageIter entry, value;
516                 const char *key;
517
518                 dbus_message_iter_recurse(&array, &entry);
519                 dbus_message_iter_get_basic(&entry, &key);
520
521                 dbus_message_iter_next(&entry);
522                 dbus_message_iter_recurse(&entry, &value);
523
524                 switch (dbus_message_iter_get_arg_type(&value)) {
525                 case DBUS_TYPE_STRING:
526                         if (g_str_equal(key, "Type") == TRUE)
527                                 dbus_message_iter_get_basic(&value, &type);
528                         else if (g_str_equal(key, "Name") == TRUE)
529                                 dbus_message_iter_get_basic(&value, &name);
530                         else if (g_str_equal(key, "Host") == TRUE)
531                                 dbus_message_iter_get_basic(&value, &host);
532                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
533                                 dbus_message_iter_get_basic(&value, &domain);
534                         break;
535                 }
536
537                 dbus_message_iter_next(&array);
538         }
539
540         if (host == NULL || domain == NULL) {
541                 err = -EINVAL;
542                 goto failed;
543         }
544
545         DBG("Type %s name %s", type, name);
546
547         if (type == NULL || name == NULL) {
548                 err = -EOPNOTSUPP;
549                 goto failed;
550         }
551
552         ident = g_strdup_printf("%s_%s", host, domain);
553         provider_dbus_ident(ident);
554
555         DBG("ident %s", ident);
556
557         provider = connman_provider_lookup(ident);
558
559         if (provider == NULL) {
560                 created = TRUE;
561                 provider = connman_provider_get(ident);
562                 if (provider) {
563                         provider->host = g_strdup(host);
564                         provider->domain = g_strdup(domain);
565                         provider->name = g_strdup(name);
566                         provider->type = g_strdup(type);
567                 }
568         }
569
570         if (provider == NULL) {
571                 DBG("can not create provider");
572                 err = -EOPNOTSUPP;
573                 goto failed;
574         }
575         dbus_message_iter_init(msg, &iter);
576         dbus_message_iter_recurse(&iter, &array);
577
578         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
579                 DBusMessageIter entry, value;
580                 const char *key, *str;
581
582                 dbus_message_iter_recurse(&array, &entry);
583                 dbus_message_iter_get_basic(&entry, &key);
584
585                 dbus_message_iter_next(&entry);
586                 dbus_message_iter_recurse(&entry, &value);
587
588                 switch (dbus_message_iter_get_arg_type(&value)) {
589                 case DBUS_TYPE_STRING:
590                         dbus_message_iter_get_basic(&value, &str);
591                         connman_provider_set_string(provider, key, str);
592                         break;
593                 }
594
595                 dbus_message_iter_next(&array);
596         }
597
598         g_free(ident);
599
600         if (provider == NULL) {
601                 err = -EOPNOTSUPP;
602                 goto failed;
603         }
604
605         if (created == TRUE)
606                 provider_probe(provider);
607
608         if (provider->vpn_service == NULL)
609                 provider->vpn_service =
610                         __connman_service_create_from_provider(provider);
611         if (provider->vpn_service == NULL) {
612                 err = -EOPNOTSUPP;
613                 goto failed;
614         }
615
616         err = __connman_service_connect(provider->vpn_service);
617         if (err < 0 && err != -EINPROGRESS)
618                 goto failed;
619
620         service_path = __connman_service_get_path(provider->vpn_service);
621         g_dbus_send_reply(connection, msg,
622                                 DBUS_TYPE_OBJECT_PATH, &service_path,
623                                                         DBUS_TYPE_INVALID);
624         return 0;
625
626 failed:
627         if (provider != NULL && created == TRUE) {
628                 DBG("can not connect delete provider");
629                 connman_provider_unref(provider);
630
631                 if (provider->vpn_service != NULL) {
632                         __connman_service_put(provider->vpn_service);
633                         provider->vpn_service = NULL;
634                 }
635         }
636
637         return err;
638 }
639
640 const char * __connman_provider_get_ident(struct connman_provider *provider)
641 {
642         if (provider == NULL)
643                 return NULL;
644
645         return provider->identifier;
646 }
647
648 int connman_provider_set_string(struct connman_provider *provider,
649                                         const char *key, const char *value)
650 {
651         DBG("provider %p key %s value %s", provider, key, value);
652
653         if (g_str_equal(key, "Type") == TRUE) {
654                 g_free(provider->type);
655                 provider->type = g_strdup(value);
656         } else if (g_str_equal(key, "Name") == TRUE) {
657                 g_free(provider->name);
658                 provider->name = g_strdup(value);
659         } else if (g_str_equal(key, "Gateway") == TRUE) {
660                 g_free(provider->element.ipv4.gateway);
661                 provider->element.ipv4.gateway = g_strdup(value);
662         } else if (g_str_equal(key, "Address") == TRUE) {
663                 g_free(provider->element.ipv4.address);
664                 provider->element.ipv4.address = g_strdup(value);
665         } else if (g_str_equal(key, "Peer") == TRUE) {
666                 g_free(provider->element.ipv4.peer);
667                 provider->element.ipv4.peer = g_strdup(value);
668         } else if (g_str_equal(key, "Netmask") == TRUE) {
669                 g_free(provider->element.ipv4.netmask);
670                 provider->element.ipv4.netmask = g_strdup(value);
671         } else if (g_str_equal(key, "PAC") == TRUE) {
672                 g_free(provider->element.ipv4.pac);
673                 provider->element.ipv4.pac = g_strdup(value);
674                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
675                                                                         value);
676         } else if (g_str_equal(key, "DNS") == TRUE) {
677                 g_free(provider->dns);
678                 provider->dns = g_strdup(value);
679         } else if (g_str_equal(key, "Domain") == TRUE) {
680                 g_free(provider->domain);
681                 provider->domain = g_strdup(value);
682         }
683
684         return connman_element_set_string(&provider->element, key, value);
685 }
686
687 const char *connman_provider_get_string(struct connman_provider *provider,
688                                                         const char *key)
689 {
690         DBG("provider %p key %s", provider, key);
691
692         if (g_str_equal(key, "Type") == TRUE)
693                 return provider->type;
694         else if (g_str_equal(key, "Name") == TRUE)
695                 return provider->name;
696
697         return connman_element_get_string(&provider->element, key);
698 }
699
700 void *connman_provider_get_data(struct connman_provider *provider)
701 {
702         return provider->driver_data;
703 }
704
705 void connman_provider_set_data(struct connman_provider *provider, void *data)
706 {
707         provider->driver_data = data;
708 }
709
710 void connman_provider_set_index(struct connman_provider *provider, int index)
711 {
712         struct connman_service *service = provider->vpn_service;
713         struct connman_ipconfig *ipconfig;
714
715         DBG("");
716
717         if (service == NULL)
718                 return;
719
720         ipconfig = __connman_service_get_ip4config(service);
721
722         if (ipconfig == NULL) {
723                 __connman_service_create_ip4config(service, index);
724
725                 ipconfig = __connman_service_get_ip4config(service);
726                 if (ipconfig == NULL) {
727                         DBG("Couldnt create ipconfig");
728                         goto done;
729                 }
730         }
731
732         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
733         __connman_ipconfig_set_index(ipconfig, index);
734
735
736         ipconfig = __connman_service_get_ip6config(service);
737
738         if (ipconfig == NULL) {
739                 __connman_service_create_ip6config(service, index);
740
741                 ipconfig = __connman_service_get_ip6config(service);
742                 if (ipconfig == NULL) {
743                         DBG("Couldnt create ipconfig for IPv6");
744                         goto done;
745                 }
746         }
747
748         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
749         __connman_ipconfig_set_index(ipconfig, index);
750
751 done:
752         provider->element.index = index;
753 }
754
755 int connman_provider_get_index(struct connman_provider *provider)
756 {
757         return provider->element.index;
758 }
759
760 int connman_provider_append_route(struct connman_provider *provider,
761                                         int family, const char *host,
762                                         const char *netmask,
763                                         const char *gateway)
764 {
765         struct connman_route *route;
766
767         if (family != AF_INET && family != AF_INET6)
768                 return -EINVAL;
769
770         route = g_try_new0(struct connman_route, 1);
771         if (route == NULL)
772                 return -ENOMEM;
773
774         route->family = family;
775         route->host = g_strdup(host);
776         route->netmask = g_strdup(netmask);
777         route->gateway = g_strdup(gateway);
778
779         provider->route_list = g_slist_append(provider->route_list, route);
780
781         return TRUE;
782 }
783
784 const char *connman_provider_get_driver_name(struct connman_provider *provider)
785 {
786         return provider->driver->name;
787 }
788
789 static gint compare_priority(gconstpointer a, gconstpointer b)
790 {
791         return 0;
792 }
793
794 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
795 {
796         struct connman_provider *provider = value;
797
798         if (provider->driver != NULL && provider->driver->remove)
799                 provider->driver->remove(provider);
800 }
801
802 int connman_provider_driver_register(struct connman_provider_driver *driver)
803 {
804         DBG("driver %p name %s", driver, driver->name);
805
806         driver_list = g_slist_insert_sorted(driver_list, driver,
807                                                         compare_priority);
808         return 0;
809 }
810
811 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
812 {
813         DBG("driver %p name %s", driver, driver->name);
814
815         driver_list = g_slist_remove(driver_list, driver);
816 }
817
818 static void provider_remove(gpointer key, gpointer value,
819                                                 gpointer user_data)
820 {
821         struct connman_provider *provider = value;
822
823         g_hash_table_remove(provider_hash, provider->identifier);
824 }
825
826 static void provider_offline_mode(connman_bool_t enabled)
827 {
828         DBG("enabled %d", enabled);
829
830         if (enabled == TRUE)
831                 g_hash_table_foreach(provider_hash, provider_remove, NULL);
832
833 }
834
835 static struct connman_notifier provider_notifier = {
836         .name                   = "provider",
837         .offline_mode           = provider_offline_mode,
838 };
839
840 int __connman_provider_init(void)
841 {
842         int err;
843
844         DBG("");
845
846         connection = connman_dbus_get_connection();
847
848         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
849                                                 NULL, unregister_provider);
850
851         err = connman_notifier_register(&provider_notifier);
852         if (err < 0) {
853                 g_hash_table_destroy(provider_hash);
854                 dbus_connection_unref(connection);
855         }
856
857         return err;
858 }
859
860 void __connman_provider_cleanup(void)
861 {
862         DBG("");
863
864         connman_notifier_unregister(&provider_notifier);
865
866         g_hash_table_foreach(provider_hash, clean_provider, NULL);
867
868         g_hash_table_destroy(provider_hash);
869         provider_hash = NULL;
870
871         dbus_connection_unref(connection);
872 }