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