Merge "Fix SIGSEV on freeing server domains list" into tizen
[platform/upstream/connman.git] / src / provider.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <gdbus.h>
31 #include <gweb/gresolv.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection = NULL;
36
37 static GHashTable *provider_hash = NULL;
38
39 static GSList *driver_list = NULL;
40
41 struct connman_provider {
42         int refcount;
43         bool immutable;
44         struct connman_service *vpn_service;
45         int index;
46         char *identifier;
47         int family;
48         struct connman_provider_driver *driver;
49         void *driver_data;
50 };
51
52 void __connman_provider_append_properties(struct connman_provider *provider,
53                                                         DBusMessageIter *iter)
54 {
55         const char *host, *domain, *type;
56         dbus_bool_t split_routing;
57
58         if (!provider->driver || !provider->driver->get_property)
59                 return;
60
61         host = provider->driver->get_property(provider, "Host");
62         domain = provider->driver->get_property(provider, "Domain");
63         type = provider->driver->get_property(provider, "Type");
64
65         if (host)
66                 connman_dbus_dict_append_basic(iter, "Host",
67                                         DBUS_TYPE_STRING, &host);
68
69         if (domain)
70                 connman_dbus_dict_append_basic(iter, "Domain",
71                                         DBUS_TYPE_STRING, &domain);
72
73         if (type)
74                 connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
75                                                  &type);
76
77         if (provider->vpn_service) {
78                 split_routing = connman_provider_is_split_routing(provider);
79                 connman_dbus_dict_append_basic(iter, "SplitRouting",
80                                         DBUS_TYPE_BOOLEAN, &split_routing);
81         }
82 }
83
84 struct connman_provider *
85 connman_provider_ref_debug(struct connman_provider *provider,
86                         const char *file, int line, const char *caller)
87 {
88         DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount + 1,
89                 file, line, caller);
90
91         __sync_fetch_and_add(&provider->refcount, 1);
92
93         return provider;
94 }
95
96 static void provider_remove(struct connman_provider *provider)
97 {
98         if (provider->driver) {
99                 provider->driver->remove(provider);
100                 provider->driver = NULL;
101         }
102 }
103
104 static void provider_destruct(struct connman_provider *provider)
105 {
106         DBG("provider %p", provider);
107
108         g_free(provider->identifier);
109         g_free(provider);
110 }
111
112 void connman_provider_unref_debug(struct connman_provider *provider,
113                                 const char *file, int line, const char *caller)
114 {
115         DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount - 1,
116                 file, line, caller);
117
118         if (__sync_fetch_and_sub(&provider->refcount, 1) != 1)
119                 return;
120
121         provider_destruct(provider);
122 }
123
124 static int provider_indicate_state(struct connman_provider *provider,
125                                         enum connman_service_state state)
126 {
127         DBG("state %d", state);
128
129         __connman_service_ipconfig_indicate_state(provider->vpn_service, state,
130                                         CONNMAN_IPCONFIG_TYPE_IPV4);
131
132         return __connman_service_ipconfig_indicate_state(provider->vpn_service,
133                                         state, CONNMAN_IPCONFIG_TYPE_IPV6);
134 }
135
136 int connman_provider_disconnect(struct connman_provider *provider)
137 {
138         int err;
139
140         DBG("provider %p", provider);
141
142         if (provider->driver && provider->driver->disconnect)
143                 err = provider->driver->disconnect(provider);
144         else
145                 return -EOPNOTSUPP;
146
147         if (provider->vpn_service)
148                 provider_indicate_state(provider,
149                                         CONNMAN_SERVICE_STATE_DISCONNECT);
150
151         if (err < 0)
152                 return err;
153
154         if (provider->vpn_service)
155                 provider_indicate_state(provider,
156                                         CONNMAN_SERVICE_STATE_IDLE);
157
158         return 0;
159 }
160
161 int connman_provider_remove(struct connman_provider *provider)
162 {
163         DBG("Removing VPN %s", provider->identifier);
164
165         provider_remove(provider);
166
167         connman_provider_set_state(provider, CONNMAN_PROVIDER_STATE_IDLE);
168
169         g_hash_table_remove(provider_hash, provider->identifier);
170
171         return 0;
172 }
173
174 int __connman_provider_connect(struct connman_provider *provider,
175                                         const char *dbus_sender)
176 {
177         int err;
178
179         DBG("provider %p", provider);
180
181         if (provider->driver && provider->driver->connect)
182                 err = provider->driver->connect(provider, dbus_sender);
183         else
184                 return -EOPNOTSUPP;
185
186         switch (err) {
187         case 0:
188                 return 0;
189
190         case -EINPROGRESS:
191                 provider_indicate_state(provider,
192                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
193                 /* fall through */
194         /*
195          * Return EINPROGRESS also for when there is an existing pending call.
196          * The state should not be indicated again but the real state is
197          * still in progress for the provider.
198          */
199         case -EALREADY:
200                 return -EINPROGRESS;
201         }
202
203         return err;
204 }
205
206 int __connman_provider_remove_by_path(const char *path)
207 {
208         struct connman_provider *provider;
209         GHashTableIter iter;
210         gpointer value, key;
211
212         DBG("path %s", path);
213
214         g_hash_table_iter_init(&iter, provider_hash);
215         while (g_hash_table_iter_next(&iter, &key, &value)) {
216                 const char *srv_path;
217                 provider = value;
218
219                 if (!provider->vpn_service)
220                         continue;
221
222                 srv_path = __connman_service_get_path(provider->vpn_service);
223
224                 if (g_strcmp0(srv_path, path) == 0) {
225                         DBG("Removing VPN %s", provider->identifier);
226
227                         provider_remove(provider);
228
229                         connman_provider_set_state(provider,
230                                                 CONNMAN_PROVIDER_STATE_IDLE);
231
232                         g_hash_table_remove(provider_hash,
233                                                 provider->identifier);
234                         return 0;
235                 }
236         }
237
238         return -ENXIO;
239 }
240
241 static int set_connected(struct connman_provider *provider,
242                                         bool connected)
243 {
244         struct connman_service *service = provider->vpn_service;
245         struct connman_ipconfig *ipconfig;
246
247         if (!service)
248                 return -ENODEV;
249
250         ipconfig = __connman_service_get_ipconfig(service, provider->family);
251
252         if (connected) {
253                 if (!ipconfig) {
254                         provider_indicate_state(provider,
255                                                 CONNMAN_SERVICE_STATE_FAILURE);
256                         return -EIO;
257                 }
258
259                 __connman_ipconfig_address_add(ipconfig);
260 #if defined TIZEN_EXT
261                 __connman_ipconfig_gateway_add(ipconfig, service);
262 #else
263                 __connman_ipconfig_gateway_add(ipconfig);
264 #endif
265
266                 provider_indicate_state(provider,
267                                         CONNMAN_SERVICE_STATE_READY);
268
269                 if (provider->driver && provider->driver->set_routes)
270                         provider->driver->set_routes(provider,
271                                                 CONNMAN_PROVIDER_ROUTE_ALL);
272
273         } else {
274                 if (ipconfig) {
275                         provider_indicate_state(provider,
276                                         CONNMAN_SERVICE_STATE_DISCONNECT);
277                         __connman_ipconfig_gateway_remove(ipconfig);
278                 }
279
280                 provider_indicate_state(provider,
281                                         CONNMAN_SERVICE_STATE_IDLE);
282         }
283
284         return 0;
285 }
286
287 int connman_provider_set_state(struct connman_provider *provider,
288                                         enum connman_provider_state state)
289 {
290         if (!provider || !provider->vpn_service)
291                 return -EINVAL;
292
293         switch (state) {
294         case CONNMAN_PROVIDER_STATE_UNKNOWN:
295                 return -EINVAL;
296         case CONNMAN_PROVIDER_STATE_IDLE:
297                 return set_connected(provider, false);
298         case CONNMAN_PROVIDER_STATE_CONNECT:
299                 return provider_indicate_state(provider,
300                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
301         case CONNMAN_PROVIDER_STATE_READY:
302                 return set_connected(provider, true);
303         case CONNMAN_PROVIDER_STATE_DISCONNECT:
304                 return provider_indicate_state(provider,
305                                         CONNMAN_SERVICE_STATE_DISCONNECT);
306         case CONNMAN_PROVIDER_STATE_FAILURE:
307                 return provider_indicate_state(provider,
308                                         CONNMAN_SERVICE_STATE_FAILURE);
309         }
310
311         return -EINVAL;
312 }
313
314 int connman_provider_indicate_error(struct connman_provider *provider,
315                                         enum connman_provider_error error)
316 {
317         enum connman_service_error service_error;
318
319         switch (error) {
320         case CONNMAN_PROVIDER_ERROR_LOGIN_FAILED:
321                 service_error = CONNMAN_SERVICE_ERROR_LOGIN_FAILED;
322                 break;
323         case CONNMAN_PROVIDER_ERROR_AUTH_FAILED:
324                 service_error = CONNMAN_SERVICE_ERROR_AUTH_FAILED;
325                 break;
326         case CONNMAN_PROVIDER_ERROR_CONNECT_FAILED:
327                 service_error = CONNMAN_SERVICE_ERROR_CONNECT_FAILED;
328                 break;
329         default:
330                 service_error = CONNMAN_SERVICE_ERROR_UNKNOWN;
331                 break;
332         }
333
334         return __connman_service_indicate_error(provider->vpn_service,
335                                                         service_error);
336 }
337
338 int connman_provider_create_service(struct connman_provider *provider)
339 {
340         if (provider->vpn_service) {
341                 bool connected;
342
343                 connected = __connman_service_is_connected_state(
344                         provider->vpn_service, CONNMAN_IPCONFIG_TYPE_IPV4);
345                 if (connected)
346                         return -EALREADY;
347
348                 connected = __connman_service_is_connected_state(
349                         provider->vpn_service, CONNMAN_IPCONFIG_TYPE_IPV6);
350                 if (connected)
351                         return -EALREADY;
352
353                 return 0;
354         }
355
356         provider->vpn_service =
357                 __connman_service_create_from_provider(provider);
358
359         if (!provider->vpn_service) {
360                 connman_warn("service creation failed for provider %s",
361                         provider->identifier);
362
363                 g_hash_table_remove(provider_hash, provider->identifier);
364                 return -EOPNOTSUPP;
365         }
366
367         return 0;
368 }
369
370 bool __connman_provider_is_immutable(struct connman_provider *provider)
371
372 {
373         if (provider)
374                 return provider->immutable;
375
376         return false;
377 }
378
379 int connman_provider_set_immutable(struct connman_provider *provider,
380                                                 bool immutable)
381 {
382         if (!provider)
383                 return -EINVAL;
384
385         provider->immutable = immutable;
386
387         return 0;
388 }
389
390 static struct connman_provider *provider_lookup(const char *identifier)
391 {
392         return g_hash_table_lookup(provider_hash, identifier);
393 }
394
395 static void connection_ready(DBusMessage *msg, int error_code, void *user_data)
396 {
397         DBusMessage *reply;
398         const char *identifier = user_data;
399
400         DBG("msg %p error %d", msg, error_code);
401
402         if (error_code != 0) {
403                 reply = __connman_error_failed(msg, -error_code);
404                 if (!g_dbus_send_message(connection, reply))
405                         DBG("reply %p send failed", reply);
406         } else {
407                 const char *path;
408                 struct connman_provider *provider;
409
410                 provider = provider_lookup(identifier);
411                 if (!provider) {
412                         reply = __connman_error_failed(msg, EINVAL);
413                         g_dbus_send_message(connection, reply);
414                         return;
415                 }
416
417                 path = __connman_service_get_path(provider->vpn_service);
418
419                 g_dbus_send_reply(connection, msg,
420                                 DBUS_TYPE_OBJECT_PATH, &path,
421                                 DBUS_TYPE_INVALID);
422         }
423 }
424
425 int __connman_provider_create_and_connect(DBusMessage *msg)
426 {
427         struct connman_provider_driver *driver;
428
429         if (!driver_list)
430                 return -EINVAL;
431
432         driver = driver_list->data;
433         if (!driver || !driver->create)
434                 return -EINVAL;
435
436         DBG("msg %p", msg);
437
438         return driver->create(msg, connection_ready);
439 }
440
441 const char *__connman_provider_get_ident(struct connman_provider *provider)
442 {
443         if (!provider)
444                 return NULL;
445
446         return provider->identifier;
447 }
448
449 const char * __connman_provider_get_transport_ident(
450                                         struct connman_provider *provider)
451 {
452         if (provider && provider && provider->driver && provider->driver->get_property)
453                 return provider->driver->get_property(provider, "Transport");
454
455         return NULL;
456 }
457
458 int connman_provider_set_string(struct connman_provider *provider,
459                                         const char *key, const char *value)
460 {
461         if (provider->driver && provider->driver->set_property)
462                 return provider->driver->set_property(provider, key, value);
463
464         return 0;
465 }
466
467 const char *connman_provider_get_string(struct connman_provider *provider,
468                                                         const char *key)
469 {
470         if (provider->driver && provider->driver->get_property)
471                 return provider->driver->get_property(provider, key);
472
473         return NULL;
474 }
475
476 bool
477 __connman_provider_check_routes(struct connman_provider *provider)
478 {
479         if (!provider)
480                 return false;
481
482         if (provider->driver && provider->driver->check_routes)
483                 return provider->driver->check_routes(provider);
484
485         return false;
486 }
487
488 void *connman_provider_get_data(struct connman_provider *provider)
489 {
490         return provider->driver_data;
491 }
492
493 void connman_provider_set_data(struct connman_provider *provider, void *data)
494 {
495         provider->driver_data = data;
496 }
497
498 void connman_provider_set_index(struct connman_provider *provider, int index)
499 {
500         struct connman_service *service = provider->vpn_service;
501         struct connman_ipconfig *ipconfig;
502
503         DBG("");
504
505         if (!service)
506                 return;
507
508         ipconfig = __connman_service_get_ip4config(service);
509
510         if (!ipconfig) {
511                 connman_service_create_ip4config(service, index);
512
513                 ipconfig = __connman_service_get_ip4config(service);
514                 if (!ipconfig) {
515                         DBG("Couldn't create ipconfig");
516                         goto done;
517                 }
518         }
519
520         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
521         __connman_ipconfig_set_index(ipconfig, index);
522
523         ipconfig = __connman_service_get_ip6config(service);
524
525         if (!ipconfig) {
526                 connman_service_create_ip6config(service, index);
527
528                 ipconfig = __connman_service_get_ip6config(service);
529                 if (!ipconfig) {
530                         DBG("Couldn't create ipconfig for IPv6");
531                         goto done;
532                 }
533         }
534
535         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
536         __connman_ipconfig_set_index(ipconfig, index);
537
538 done:
539         provider->index = index;
540 }
541
542 int connman_provider_get_index(struct connman_provider *provider)
543 {
544         return provider->index;
545 }
546
547 int connman_provider_set_ipaddress(struct connman_provider *provider,
548                                         struct connman_ipaddress *ipaddress)
549 {
550         struct connman_ipconfig *ipconfig = NULL;
551
552         ipconfig = __connman_service_get_ipconfig(provider->vpn_service,
553                                                         ipaddress->family);
554         if (!ipconfig)
555                 return -EINVAL;
556
557         provider->family = ipaddress->family;
558
559         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
560
561         __connman_ipconfig_set_local(ipconfig, ipaddress->local);
562         __connman_ipconfig_set_peer(ipconfig, ipaddress->peer);
563         __connman_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
564         __connman_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
565         __connman_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
566
567         return 0;
568 }
569
570 int connman_provider_set_pac(struct connman_provider *provider, const char *pac)
571 {
572         DBG("provider %p pac %s", provider, pac);
573
574         __connman_service_set_pac(provider->vpn_service, pac);
575
576         return 0;
577 }
578
579
580 int connman_provider_set_domain(struct connman_provider *provider,
581                                         const char *domain)
582 {
583         DBG("provider %p domain %s", provider, domain);
584
585         __connman_service_set_domainname(provider->vpn_service, domain);
586
587         return 0;
588 }
589
590 int connman_provider_set_nameservers(struct connman_provider *provider,
591                                         char * const *nameservers)
592 {
593         int i;
594
595         DBG("provider %p nameservers %p", provider, nameservers);
596
597         __connman_service_nameserver_clear(provider->vpn_service);
598
599         if (!nameservers)
600                 return 0;
601
602         for (i = 0; nameservers[i]; i++)
603 #if defined TIZEN_EXT
604                 __connman_service_nameserver_append(provider->vpn_service,
605                                                 nameservers[i], false,
606                                                 CONNMAN_IPCONFIG_TYPE_ALL);
607 #else
608                 __connman_service_nameserver_append(provider->vpn_service,
609                                                 nameservers[i], false);
610 #endif
611
612         return 0;
613 }
614
615 void connman_provider_set_autoconnect(struct connman_provider *provider,
616                                                                 bool flag)
617 {
618         if (!provider || !provider->vpn_service)
619                 return;
620
621         /* Save VPN service if autoconnect value changes */
622         if (connman_service_set_autoconnect(provider->vpn_service, flag))
623                 __connman_service_save(provider->vpn_service);
624 }
625
626 bool connman_provider_is_split_routing(struct connman_provider *provider)
627 {
628         if (!provider || !provider->vpn_service)
629                 return false;
630
631         return __connman_service_is_split_routing(provider->vpn_service);
632 }
633
634 int connman_provider_set_split_routing(struct connman_provider *provider,
635                                                         bool split_routing)
636 {
637         struct connman_service *service;
638         enum connman_ipconfig_type type;
639         int service_index;
640         int vpn_index;
641         bool service_split_routing;
642         int err = 0;
643
644         DBG("");
645
646         if (!provider || !provider->vpn_service)
647                 return -EINVAL;
648
649         service_split_routing = __connman_service_is_split_routing(
650                                 provider->vpn_service);
651
652         if (service_split_routing == split_routing) {
653                 DBG("split_routing already set %s",
654                                         split_routing ? "true" : "false");
655                 return -EALREADY;
656         }
657
658         switch (provider->family) {
659         case AF_INET:
660                 type = CONNMAN_IPCONFIG_TYPE_IPV4;
661                 break;
662         case AF_INET6:
663                 type = CONNMAN_IPCONFIG_TYPE_IPV6;
664                 break;
665         case AF_UNSPEC:
666                 type = CONNMAN_IPCONFIG_TYPE_ALL;
667                 break;
668         default:
669                 type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
670         }
671
672         if (!__connman_service_is_connected_state(provider->vpn_service,
673                                                                 type)) {
674                 DBG("%p VPN not connected", provider->vpn_service);
675                 goto save;
676         }
677
678         vpn_index = __connman_service_get_index(provider->vpn_service);
679         service_index = __connman_connection_get_vpn_phy_index(vpn_index);
680         service = __connman_service_lookup_from_index(service_index);
681         if (!service)
682                 goto save;
683
684         if (split_routing)
685                 err = __connman_service_move(service, provider->vpn_service,
686                                         true);
687         else
688                 err = __connman_service_move(provider->vpn_service, service,
689                                         true);
690
691         if (err) {
692                 connman_warn("cannot move service %p and VPN %p error %d",
693                                         service, provider->vpn_service, err);
694
695                 /*
696                  * In case of error notify vpnd about the current split routing
697                  * state.
698                  */
699                 __connman_service_split_routing_changed(provider->vpn_service);
700                 goto out;
701         }
702
703 save:
704         __connman_service_set_split_routing(provider->vpn_service,
705                                                                 split_routing);
706         __connman_service_save(provider->vpn_service);
707
708 out:
709         return err;
710 }
711
712 int connman_provider_get_family(struct connman_provider *provider)
713 {
714         if (!provider)
715                 return AF_UNSPEC;
716
717         return provider->family;
718 }
719
720 static void unregister_provider(gpointer data)
721 {
722         struct connman_provider *provider = data;
723
724         DBG("provider %p service %p", provider, provider->vpn_service);
725
726         if (provider->vpn_service) {
727                 connman_service_unref(provider->vpn_service);
728                 provider->vpn_service = NULL;
729         }
730
731         connman_provider_unref(provider);
732 }
733
734 static gint compare_priority(gconstpointer a, gconstpointer b)
735 {
736         return 0;
737 }
738
739 int connman_provider_driver_register(struct connman_provider_driver *driver)
740 {
741         DBG("driver %p name %s", driver, driver->name);
742
743         driver_list = g_slist_insert_sorted(driver_list, driver,
744                                                         compare_priority);
745         return 0;
746 }
747
748 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
749 {
750         DBG("driver %p name %s", driver, driver->name);
751
752         driver_list = g_slist_remove(driver_list, driver);
753 }
754
755 void connman_provider_set_driver(struct connman_provider *provider,
756                                 struct connman_provider_driver *driver)
757 {
758         provider->driver = driver;
759 }
760
761 static void provider_disconnect_all(gpointer key, gpointer value,
762                                                 gpointer user_data)
763 {
764         struct connman_provider *provider = value;
765
766         connman_provider_disconnect(provider);
767 }
768
769 static void provider_offline_mode(bool enabled)
770 {
771         DBG("enabled %d", enabled);
772
773         if (enabled)
774                 g_hash_table_foreach(provider_hash, provider_disconnect_all,
775                                                                         NULL);
776
777 }
778
779 static void provider_initialize(struct connman_provider *provider)
780 {
781         DBG("provider %p", provider);
782
783         provider->index = 0;
784         provider->identifier = NULL;
785 }
786
787 static struct connman_provider *provider_new(void)
788 {
789         struct connman_provider *provider;
790
791         provider = g_try_new0(struct connman_provider, 1);
792         if (!provider)
793                 return NULL;
794
795         provider->refcount = 1;
796
797         DBG("provider %p", provider);
798         provider_initialize(provider);
799
800         return provider;
801 }
802
803 struct connman_provider *connman_provider_get(const char *identifier)
804 {
805         struct connman_provider *provider;
806
807         provider = g_hash_table_lookup(provider_hash, identifier);
808         if (provider)
809                 return provider;
810
811         provider = provider_new();
812         if (!provider)
813                 return NULL;
814
815         DBG("provider %p", provider);
816
817         provider->identifier = g_strdup(identifier);
818
819         g_hash_table_insert(provider_hash, provider->identifier, provider);
820
821         return provider;
822 }
823
824 void connman_provider_put(struct connman_provider *provider)
825 {
826         g_hash_table_remove(provider_hash, provider->identifier);
827 }
828
829 static struct connman_provider *provider_get(int index)
830 {
831         GHashTableIter iter;
832         gpointer value, key;
833
834         g_hash_table_iter_init(&iter, provider_hash);
835
836         while (g_hash_table_iter_next(&iter, &key, &value)) {
837                 struct connman_provider *provider = value;
838
839                 if (provider->index == index)
840                         return provider;
841         }
842
843         return NULL;
844 }
845
846 static void provider_service_changed(struct connman_service *service,
847                                 enum connman_service_state state)
848 {
849         struct connman_provider *provider;
850         int vpn_index, service_index;
851
852         if (!service)
853                 return;
854
855         switch (state) {
856         case CONNMAN_SERVICE_STATE_UNKNOWN:
857         case CONNMAN_SERVICE_STATE_IDLE:
858         case CONNMAN_SERVICE_STATE_ASSOCIATION:
859         case CONNMAN_SERVICE_STATE_CONFIGURATION:
860         case CONNMAN_SERVICE_STATE_READY:
861         case CONNMAN_SERVICE_STATE_ONLINE:
862                 return;
863         case CONNMAN_SERVICE_STATE_DISCONNECT:
864         case CONNMAN_SERVICE_STATE_FAILURE:
865                 break;
866         }
867
868         service_index = __connman_service_get_index(service);
869
870         vpn_index = __connman_connection_get_vpn_index(service_index);
871
872         DBG("service %p %s state %d index %d/%d", service,
873                 connman_service_get_identifier(service),
874                 state, service_index, vpn_index);
875
876         if (vpn_index < 0)
877                 return;
878
879         provider = provider_get(vpn_index);
880         if (!provider)
881                 return;
882
883         DBG("disconnect %p index %d", provider, vpn_index);
884
885         connman_provider_disconnect(provider);
886 }
887
888 static const struct connman_notifier provider_notifier = {
889         .name                   = "provider",
890         .offline_mode           = provider_offline_mode,
891         .service_state_changed  = provider_service_changed,
892 };
893
894 int __connman_provider_init(void)
895 {
896         int err;
897
898         DBG("");
899
900         connection = connman_dbus_get_connection();
901
902         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
903                                                 NULL, unregister_provider);
904
905         err = connman_notifier_register(&provider_notifier);
906         if (err < 0) {
907                 g_hash_table_destroy(provider_hash);
908                 dbus_connection_unref(connection);
909         }
910
911         return err;
912 }
913
914 void __connman_provider_cleanup(void)
915 {
916         DBG("");
917
918         connman_notifier_unregister(&provider_notifier);
919
920         g_hash_table_destroy(provider_hash);
921         provider_hash = NULL;
922
923         dbus_connection_unref(connection);
924 }