6d3827b70e786247eee819747700703a304e4a50
[platform/upstream/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011  BWM CarIT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28
29 #include <gdbus.h>
30
31 #include <connman/session.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection;
36 static GHashTable *session_hash;
37 static connman_bool_t sessionmode;
38 static struct connman_session *ecall_session;
39 static GSList *policy_list;
40
41 enum connman_session_trigger {
42         CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
43         CONNMAN_SESSION_TRIGGER_SETTING         = 1,
44         CONNMAN_SESSION_TRIGGER_CONNECT         = 2,
45         CONNMAN_SESSION_TRIGGER_DISCONNECT      = 3,
46         CONNMAN_SESSION_TRIGGER_SERVICE         = 4,
47         CONNMAN_SESSION_TRIGGER_ECALL           = 5,
48 };
49
50 enum connman_session_reason {
51         CONNMAN_SESSION_REASON_UNKNOWN          = 0,
52         CONNMAN_SESSION_REASON_CONNECT          = 1,
53         CONNMAN_SESSION_REASON_FREE_RIDE        = 2,
54 };
55
56 enum connman_session_state {
57         CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
58         CONNMAN_SESSION_STATE_CONNECTED      = 1,
59         CONNMAN_SESSION_STATE_ONLINE         = 2,
60 };
61
62 struct service_entry {
63         struct connman_session *session;
64         /* track why this service was selected */
65         enum connman_session_reason reason;
66         enum connman_service_state state;
67         const char *name;
68         struct connman_service *service;
69         char *ifname;
70         const char *bearer;
71         GSList *pending_timeouts;
72 };
73
74 struct session_info {
75         struct connman_session_config config;
76         enum connman_session_state state;
77         struct service_entry *entry;
78         enum connman_session_reason reason;
79 };
80
81 struct connman_session {
82         char *owner;
83         char *session_path;
84         char *notify_path;
85         guint notify_watch;
86
87         struct connman_session_policy *policy;
88
89         connman_bool_t append_all;
90         struct session_info *info;
91         struct session_info *info_last;
92         struct connman_session_config *policy_config;
93         GSList *user_allowed_bearers;
94
95         connman_bool_t ecall;
96
97         GList *service_list;
98         GHashTable *service_hash;
99 };
100
101 static const char *trigger2string(enum connman_session_trigger trigger)
102 {
103         switch (trigger) {
104         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
105                 break;
106         case CONNMAN_SESSION_TRIGGER_SETTING:
107                 return "setting";
108         case CONNMAN_SESSION_TRIGGER_CONNECT:
109                 return "connect";
110         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
111                 return "disconnect";
112         case CONNMAN_SESSION_TRIGGER_SERVICE:
113                 return "service";
114         case CONNMAN_SESSION_TRIGGER_ECALL:
115                 return "ecall";
116         }
117
118         return NULL;
119 }
120
121 static const char *reason2string(enum connman_session_reason reason)
122 {
123         switch (reason) {
124         case CONNMAN_SESSION_REASON_UNKNOWN:
125                 return "unknown";
126         case CONNMAN_SESSION_REASON_CONNECT:
127                 return "connect";
128         case CONNMAN_SESSION_REASON_FREE_RIDE:
129                 return "free-ride";
130         }
131
132         return NULL;
133 }
134
135 static const char *state2string(enum connman_session_state state)
136 {
137         switch (state) {
138         case CONNMAN_SESSION_STATE_DISCONNECTED:
139                 return "disconnected";
140         case CONNMAN_SESSION_STATE_CONNECTED:
141                 return "connected";
142         case CONNMAN_SESSION_STATE_ONLINE:
143                 return "online";
144         }
145
146         return NULL;
147 }
148
149 static const char *type2string(enum connman_session_type type)
150 {
151         switch (type) {
152         case CONNMAN_SESSION_TYPE_UNKNOWN:
153                 return "";
154         case CONNMAN_SESSION_TYPE_ANY:
155                 return "any";
156         case CONNMAN_SESSION_TYPE_LOCAL:
157                 return "local";
158         case CONNMAN_SESSION_TYPE_INTERNET:
159                 return "internet";
160         }
161
162         return NULL;
163 }
164
165 enum connman_session_roaming_policy connman_session_parse_roaming_policy(const char *policy)
166 {
167         if (g_strcmp0(policy, "default") == 0)
168                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
169         else if (g_strcmp0(policy, "always") == 0)
170                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
171         else if (g_strcmp0(policy, "forbidden") == 0)
172                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
173         else if (g_strcmp0(policy, "national") == 0)
174                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
175         else if (g_strcmp0(policy, "international") == 0)
176                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
177         else
178                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
179 }
180
181 enum connman_session_type connman_session_parse_connection_type(const char *type)
182 {
183         if (g_strcmp0(type, "any") == 0)
184                 return CONNMAN_SESSION_TYPE_ANY;
185         if (g_strcmp0(type, "local") == 0)
186                 return CONNMAN_SESSION_TYPE_LOCAL;
187         else if (g_strcmp0(type, "internet") == 0)
188                 return CONNMAN_SESSION_TYPE_INTERNET;
189
190         return CONNMAN_SESSION_TYPE_UNKNOWN;
191 }
192
193 static int bearer2service(const char *bearer, enum connman_service_type *type)
194 {
195         if (g_strcmp0(bearer, "ethernet") == 0)
196                 *type = CONNMAN_SERVICE_TYPE_ETHERNET;
197         else if (g_strcmp0(bearer, "wifi") == 0)
198                 *type = CONNMAN_SERVICE_TYPE_WIFI;
199         else if (g_strcmp0(bearer, "bluetooth") == 0)
200                 *type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
201         else if (g_strcmp0(bearer, "cellular") == 0)
202                 *type = CONNMAN_SERVICE_TYPE_CELLULAR;
203         else if (g_strcmp0(bearer, "vpn") == 0)
204                 *type = CONNMAN_SERVICE_TYPE_VPN;
205         else if (g_strcmp0(bearer, "*") == 0)
206                 *type = CONNMAN_SERVICE_TYPE_UNKNOWN;
207         else
208                 return -EINVAL;
209
210         return 0;
211 }
212
213 static char *service2bearer(enum connman_service_type type)
214 {
215         switch (type) {
216         case CONNMAN_SERVICE_TYPE_ETHERNET:
217                 return "ethernet";
218         case CONNMAN_SERVICE_TYPE_WIFI:
219                 return "wifi";
220         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
221                 return "bluetooth";
222         case CONNMAN_SERVICE_TYPE_CELLULAR:
223                 return "cellular";
224         case CONNMAN_SERVICE_TYPE_VPN:
225                 return "vpn";
226         case CONNMAN_SERVICE_TYPE_UNKNOWN:
227                 return "*";
228         case CONNMAN_SERVICE_TYPE_SYSTEM:
229         case CONNMAN_SERVICE_TYPE_GPS:
230         case CONNMAN_SERVICE_TYPE_GADGET:
231                 return "";
232         }
233
234         return "";
235 }
236
237 static void destroy_policy_config(struct connman_session *session)
238 {
239         if (session->policy == NULL) {
240                 g_free(session->policy_config);
241                 return;
242         }
243
244         (*session->policy->destroy)(session);
245 }
246
247 static void free_session(struct connman_session *session)
248 {
249         if (session == NULL)
250                 return;
251
252         if (session->notify_watch > 0)
253                 g_dbus_remove_watch(connection, session->notify_watch);
254
255         destroy_policy_config(session);
256         g_slist_free(session->info->config.allowed_bearers);
257         g_free(session->owner);
258         g_free(session->session_path);
259         g_free(session->notify_path);
260         g_free(session->info);
261         g_free(session->info_last);
262
263         g_free(session);
264 }
265
266 static void cleanup_session(gpointer user_data)
267 {
268         struct connman_session *session = user_data;
269         struct session_info *info = session->info;
270
271         DBG("remove %s", session->session_path);
272
273         g_slist_free(session->user_allowed_bearers);
274         if (session->service_hash != NULL)
275                 g_hash_table_destroy(session->service_hash);
276         g_list_free(session->service_list);
277
278         if (info->entry != NULL &&
279                         info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
280                 __connman_service_disconnect(info->entry->service);
281         }
282
283         free_session(session);
284 }
285
286 static int assign_policy_plugin(struct connman_session *session)
287 {
288         if (session->policy != NULL)
289                 return -EALREADY;
290
291         if (policy_list == NULL)
292                 return 0;
293
294         session->policy = policy_list->data;
295
296         return 0;
297 }
298
299 struct user_config {
300         DBusMessage *pending;
301
302         enum connman_session_type type;
303         GSList *allowed_bearers;
304 };
305
306 static void cleanup_user_config(struct user_config *user_config)
307 {
308         if (user_config == NULL)
309                 return;
310
311         if (user_config->pending != NULL)
312                 dbus_message_unref(user_config->pending);
313
314         g_slist_free(user_config->allowed_bearers);
315         g_free(user_config);
316 }
317
318 static int create_policy_config(struct connman_session *session,
319                                 connman_session_config_func_t cb,
320                                 struct user_config *user_config)
321 {
322         struct connman_session_config *config;
323
324         if (session->policy == NULL) {
325                 config = connman_session_create_default_config();
326                 if (config == NULL) {
327                         free_session(session);
328                         cleanup_user_config(user_config);
329                         return -ENOMEM;
330                 }
331
332                 return cb(session, config, user_config, 0);
333         }
334
335         return (*session->policy->create)(session, cb, user_config);
336 }
337
338 static void probe_policy(struct connman_session_policy *policy)
339 {
340
341         GHashTableIter iter;
342         gpointer key, value;
343         struct connman_session *session;
344
345         DBG("policy %p name %s", policy, policy->name);
346
347         g_hash_table_iter_init(&iter, session_hash);
348
349         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
350                 session = value;
351
352                 if (session->policy != NULL)
353                         continue;
354
355                 assign_policy_plugin(session);
356         }
357 }
358
359 static void remove_policy(struct connman_session_policy *policy)
360 {
361         GHashTableIter iter;
362         gpointer key, value;
363         struct connman_session *session;
364
365         if (session_hash == NULL)
366                 return;
367
368         DBG("policy %p name %s", policy, policy->name);
369
370         g_hash_table_iter_init(&iter, session_hash);
371
372         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
373                 session = value;
374
375                 if (session->policy != policy)
376                         continue;
377
378                 session->policy = NULL;
379                 assign_policy_plugin(session);
380         }
381 }
382
383 static gint compare_priority(gconstpointer a, gconstpointer b)
384 {
385         const struct connman_session_policy *policy1 = a;
386         const struct connman_session_policy *policy2 = b;
387
388         return policy2->priority - policy1->priority;
389 }
390
391
392 int connman_session_policy_register(struct connman_session_policy *policy)
393 {
394         DBG("name %s", policy->name);
395
396         if (policy->create == NULL || policy->destroy == NULL)
397                 return -EINVAL;
398
399         policy_list = g_slist_insert_sorted(policy_list, policy,
400                                                 compare_priority);
401
402         probe_policy(policy);
403
404         return 0;
405 }
406
407 void connman_session_policy_unregister(struct connman_session_policy *policy)
408 {
409         DBG("name %s", policy->name);
410
411         policy_list = g_slist_remove(policy_list, policy);
412
413         remove_policy(policy);
414 }
415
416 void connman_session_set_default_config(struct connman_session_config *config)
417 {
418         config->id_type = CONNMAN_SESSION_ID_TYPE_UNKNOWN;
419         g_free(config->id);
420         config->id = NULL;
421
422         config->priority = FALSE;
423         config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
424         config->type = CONNMAN_SESSION_TYPE_ANY;
425         config->ecall = FALSE;
426
427         g_slist_free(config->allowed_bearers);
428         config->allowed_bearers = g_slist_prepend(NULL,
429                                 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
430 }
431
432 struct connman_session_config *connman_session_create_default_config(void)
433 {
434         struct connman_session_config *config;
435
436         config = g_new0(struct connman_session_config, 1);
437         connman_session_set_default_config(config);
438
439         return config;
440 }
441
442 static enum connman_session_type apply_policy_on_type(
443                         enum connman_session_type policy,
444                         enum connman_session_type type)
445 {
446         if (type == CONNMAN_SESSION_TYPE_UNKNOWN)
447                 return CONNMAN_SESSION_TYPE_UNKNOWN;
448
449         if (policy == CONNMAN_SESSION_TYPE_ANY)
450                 return type;
451
452         if (policy == CONNMAN_SESSION_TYPE_LOCAL)
453                 return CONNMAN_SESSION_TYPE_LOCAL;
454
455         return CONNMAN_SESSION_TYPE_INTERNET;
456 }
457
458 int connman_session_parse_bearers(const char *token, GSList **list)
459 {
460         enum connman_service_type bearer;
461         int err;
462
463         if (g_strcmp0(token, "") == 0)
464                 return 0;
465
466         err = bearer2service(token, &bearer);
467         if (err < 0)
468                 return err;
469
470         *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
471
472         return 0;
473 }
474
475 static int parse_bearers(DBusMessageIter *iter, GSList **list)
476 {
477         DBusMessageIter array;
478         int type, err;
479
480         dbus_message_iter_recurse(iter, &array);
481
482         *list = NULL;
483
484         while ((type = dbus_message_iter_get_arg_type(&array)) !=
485                         DBUS_TYPE_INVALID) {
486                 char *bearer_name = NULL;
487
488                 if (type != DBUS_TYPE_STRING) {
489                         g_slist_free(*list);
490                         *list = NULL;
491                         return -EINVAL;
492                 }
493
494                 dbus_message_iter_get_basic(&array, &bearer_name);
495
496                 err = connman_session_parse_bearers(bearer_name, list);
497                 if (err < 0) {
498                         g_slist_free(*list);
499                         *list = NULL;
500                         return err;
501                 }
502
503                 dbus_message_iter_next(&array);
504         }
505
506         return 0;
507 }
508
509 static int filter_bearer(GSList *policy_bearers,
510                                 enum connman_service_type bearer,
511                                 GSList **list)
512 {
513         enum connman_service_type policy;
514         GSList *it;
515
516         if (policy_bearers == NULL)
517                 return 0;
518
519         for (it = policy_bearers; it != NULL; it = it->next) {
520                 policy = GPOINTER_TO_INT(it->data);
521
522                 if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) {
523                         bearer = policy;
524                         goto clone;
525                 }
526
527                 if (policy != CONNMAN_SERVICE_TYPE_UNKNOWN && policy != bearer)
528                         continue;
529
530                 goto clone;
531         }
532
533         *list = NULL;
534
535         return 0;
536
537 clone:
538         *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
539
540         return 0;
541 }
542
543 static int apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers,
544                                 GSList **list)
545 {
546         enum connman_service_type bearer;
547         GSList *it;
548         int err;
549
550         *list = NULL;
551
552         for (it = bearers; it != NULL; it = it->next) {
553                 bearer = GPOINTER_TO_INT(it->data);
554
555                 err = filter_bearer(policy_bearers, bearer, list);
556                 if (err < 0)
557                         return err;
558         }
559
560         return 0;
561 }
562
563 const char *connman_session_get_owner(struct connman_session *session)
564 {
565         return session->owner;
566 }
567
568 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
569 {
570         struct session_info *info = user_data;
571         GSList *list;
572
573         for (list = info->config.allowed_bearers;
574                         list != NULL; list = list->next) {
575                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
576                 const char *name = __connman_service_type2string(bearer);
577
578                 if (name == NULL)
579                         name = "*";
580
581                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
582                                                 &name);
583         }
584 }
585
586 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
587 {
588         struct connman_service *service = user_data;
589         struct connman_ipconfig *ipconfig_ipv4;
590
591         if (service == NULL)
592                 return;
593
594         if (__connman_service_is_connected_state(service,
595                                 CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
596                 return;
597         }
598
599         ipconfig_ipv4 = __connman_service_get_ip4config(service);
600         if (ipconfig_ipv4 == NULL)
601                 return;
602
603         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
604 }
605
606 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
607 {
608         struct connman_service *service = user_data;
609         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
610
611         if (service == NULL)
612                 return;
613
614         if (__connman_service_is_connected_state(service,
615                                 CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
616                 return;
617         }
618
619         ipconfig_ipv4 = __connman_service_get_ip4config(service);
620         ipconfig_ipv6 = __connman_service_get_ip6config(service);
621         if (ipconfig_ipv6 == NULL)
622                 return;
623
624         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
625 }
626
627 static void append_notify(DBusMessageIter *dict,
628                                         struct connman_session *session)
629 {
630         struct session_info *info = session->info;
631         struct session_info *info_last = session->info_last;
632         struct connman_service *service;
633         const char *name, *ifname, *bearer;
634
635         if (session->append_all == TRUE ||
636                         info->state != info_last->state) {
637                 const char *state = state2string(info->state);
638
639                 connman_dbus_dict_append_basic(dict, "State",
640                                                 DBUS_TYPE_STRING,
641                                                 &state);
642                 info_last->state = info->state;
643         }
644
645         if (session->append_all == TRUE ||
646                         info->entry != info_last->entry) {
647                 if (info->entry == NULL) {
648                         name = "";
649                         ifname = "";
650                         service = NULL;
651                         bearer = "";
652                 } else {
653                         name = info->entry->name;
654                         ifname = info->entry->ifname;
655                         service = info->entry->service;
656                         bearer = info->entry->bearer;
657                 }
658
659                 connman_dbus_dict_append_basic(dict, "Name",
660                                                 DBUS_TYPE_STRING,
661                                                 &name);
662
663                 connman_dbus_dict_append_dict(dict, "IPv4",
664                                                 append_ipconfig_ipv4,
665                                                 service);
666
667                 connman_dbus_dict_append_dict(dict, "IPv6",
668                                                 append_ipconfig_ipv6,
669                                                 service);
670
671                 connman_dbus_dict_append_basic(dict, "Interface",
672                                                 DBUS_TYPE_STRING,
673                                                 &ifname);
674
675                 connman_dbus_dict_append_basic(dict, "Bearer",
676                                                 DBUS_TYPE_STRING,
677                                                 &bearer);
678
679                 info_last->entry = info->entry;
680         }
681
682         if (session->append_all == TRUE ||
683                         info->config.type != info_last->config.type) {
684                 const char *type = type2string(info->config.type);
685
686                 connman_dbus_dict_append_basic(dict, "ConnectionType",
687                                                 DBUS_TYPE_STRING,
688                                                 &type);
689                 info_last->config.type = info->config.type;
690         }
691
692         if (session->append_all == TRUE ||
693                         info->config.allowed_bearers != info_last->config.allowed_bearers) {
694                 connman_dbus_dict_append_array(dict, "AllowedBearers",
695                                                 DBUS_TYPE_STRING,
696                                                 append_allowed_bearers,
697                                                 info);
698                 info_last->config.allowed_bearers = info->config.allowed_bearers;
699         }
700
701         session->append_all = FALSE;
702 }
703
704 static connman_bool_t is_type_matching_state(enum connman_session_state *state,
705                                                 enum connman_session_type type)
706 {
707         switch (type) {
708         case CONNMAN_SESSION_TYPE_UNKNOWN:
709                 return FALSE;
710         case CONNMAN_SESSION_TYPE_ANY:
711                 return TRUE;
712         case CONNMAN_SESSION_TYPE_LOCAL:
713                 if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
714                         *state = CONNMAN_SESSION_STATE_CONNECTED;
715                         return TRUE;
716                 }
717
718                 break;
719         case CONNMAN_SESSION_TYPE_INTERNET:
720                 if (*state == CONNMAN_SESSION_STATE_ONLINE)
721                         return TRUE;
722                 break;
723         }
724
725         return FALSE;
726 }
727
728 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
729 {
730         struct session_info *info_last = session->info_last;
731         struct session_info *info = session->info;
732
733         if (session->append_all == TRUE)
734                 return TRUE;
735
736         if (info->state != info_last->state)
737                 return TRUE;
738
739         if (info->entry != info_last->entry &&
740                         info->state >= CONNMAN_SESSION_STATE_CONNECTED)
741                 return TRUE;
742
743         if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
744                         info->config.type != info_last->config.type)
745                 return TRUE;
746
747         return FALSE;
748 }
749
750 static gboolean session_notify(gpointer user_data)
751 {
752         struct connman_session *session = user_data;
753         DBusMessage *msg;
754         DBusMessageIter array, dict;
755
756         if (compute_notifiable_changes(session) == FALSE)
757                 return FALSE;
758
759         DBG("session %p owner %s notify_path %s", session,
760                 session->owner, session->notify_path);
761
762         msg = dbus_message_new_method_call(session->owner, session->notify_path,
763                                                 CONNMAN_NOTIFICATION_INTERFACE,
764                                                 "Update");
765         if (msg == NULL)
766                 return FALSE;
767
768         dbus_message_iter_init_append(msg, &array);
769         connman_dbus_dict_open(&array, &dict);
770
771         append_notify(&dict, session);
772
773         connman_dbus_dict_close(&array, &dict);
774
775         g_dbus_send_message(connection, msg);
776
777         return FALSE;
778 }
779
780 static void ipconfig_ipv4_changed(struct connman_session *session)
781 {
782         struct session_info *info = session->info;
783
784         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
785                                                 "IPv4", append_ipconfig_ipv4,
786                                                 info->entry->service);
787 }
788
789 static void ipconfig_ipv6_changed(struct connman_session *session)
790 {
791         struct session_info *info = session->info;
792
793         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
794                                                 "IPv6", append_ipconfig_ipv6,
795                                                 info->entry->service);
796 }
797
798 static connman_bool_t service_type_match(struct connman_session *session,
799                                         struct connman_service *service)
800 {
801         struct session_info *info = session->info;
802         GSList *list;
803
804         for (list = info->config.allowed_bearers;
805                         list != NULL; list = list->next) {
806                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
807                 enum connman_service_type service_type;
808
809                 if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN)
810                         return TRUE;
811
812                 service_type = connman_service_get_type(service);
813                 if (bearer == service_type)
814                         return TRUE;
815         }
816
817         return FALSE;
818 }
819
820 static connman_bool_t service_match(struct connman_session *session,
821                                         struct connman_service *service)
822 {
823         if (service_type_match(session, service) == FALSE)
824                 return FALSE;
825
826         return TRUE;
827 }
828
829 static int service_type_weight(enum connman_service_type type)
830 {
831         /*
832          * The session doesn't care which service
833          * to use. Nevertheless we have to sort them
834          * according their type. The ordering is
835          *
836          * 1. Ethernet
837          * 2. Bluetooth
838          * 3. WiFi
839          * 4. Cellular
840          */
841
842         switch (type) {
843         case CONNMAN_SERVICE_TYPE_ETHERNET:
844                 return 4;
845         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
846                 return 3;
847         case CONNMAN_SERVICE_TYPE_WIFI:
848                 return 2;
849         case CONNMAN_SERVICE_TYPE_CELLULAR:
850                 return 1;
851         case CONNMAN_SERVICE_TYPE_UNKNOWN:
852         case CONNMAN_SERVICE_TYPE_SYSTEM:
853         case CONNMAN_SERVICE_TYPE_GPS:
854         case CONNMAN_SERVICE_TYPE_VPN:
855         case CONNMAN_SERVICE_TYPE_GADGET:
856                 break;
857         }
858
859         return 0;
860 }
861
862 static gint sort_allowed_bearers(struct connman_service *service_a,
863                                         struct connman_service *service_b,
864                                         struct connman_session *session)
865 {
866         struct session_info *info = session->info;
867         GSList *list;
868         enum connman_service_type type_a, type_b;
869         int weight_a, weight_b;
870
871         type_a = connman_service_get_type(service_a);
872         type_b = connman_service_get_type(service_b);
873
874         for (list = info->config.allowed_bearers;
875                         list != NULL; list = list->next) {
876                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
877
878                 if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) {
879                         if (type_a != type_b) {
880                                 weight_a = service_type_weight(type_a);
881                                 weight_b = service_type_weight(type_b);
882
883                                 if (weight_a > weight_b)
884                                         return -1;
885
886                                 if (weight_a < weight_b)
887                                         return 1;
888
889                                 return 0;
890                         }
891                 }
892
893                 if (type_a == bearer && type_b == bearer)
894                         return 0;
895
896                 if (type_a == bearer && type_b != bearer)
897                         return -1;
898
899                 if (type_a != bearer && type_b == bearer)
900                         return 1;
901         }
902
903         return 0;
904 }
905
906 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
907 {
908         struct service_entry *entry_a = (void *)a;
909         struct service_entry *entry_b = (void *)b;
910         struct connman_session *session = user_data;
911
912         return sort_allowed_bearers(entry_a->service, entry_b->service,
913                                 session);
914 }
915
916 static enum connman_session_state service_to_session_state(enum connman_service_state state)
917 {
918         switch (state) {
919         case CONNMAN_SERVICE_STATE_UNKNOWN:
920         case CONNMAN_SERVICE_STATE_IDLE:
921         case CONNMAN_SERVICE_STATE_ASSOCIATION:
922         case CONNMAN_SERVICE_STATE_CONFIGURATION:
923         case CONNMAN_SERVICE_STATE_DISCONNECT:
924         case CONNMAN_SERVICE_STATE_FAILURE:
925                 break;
926         case CONNMAN_SERVICE_STATE_READY:
927                 return CONNMAN_SESSION_STATE_CONNECTED;
928         case CONNMAN_SERVICE_STATE_ONLINE:
929                 return CONNMAN_SESSION_STATE_ONLINE;
930         }
931
932         return CONNMAN_SESSION_STATE_DISCONNECTED;
933 }
934
935 static connman_bool_t is_connected(enum connman_service_state state)
936 {
937         switch (state) {
938         case CONNMAN_SERVICE_STATE_UNKNOWN:
939         case CONNMAN_SERVICE_STATE_IDLE:
940         case CONNMAN_SERVICE_STATE_ASSOCIATION:
941         case CONNMAN_SERVICE_STATE_CONFIGURATION:
942         case CONNMAN_SERVICE_STATE_DISCONNECT:
943         case CONNMAN_SERVICE_STATE_FAILURE:
944                 break;
945         case CONNMAN_SERVICE_STATE_READY:
946         case CONNMAN_SERVICE_STATE_ONLINE:
947                 return TRUE;
948         }
949
950         return FALSE;
951 }
952
953 static connman_bool_t is_connecting(enum connman_service_state state)
954 {
955         switch (state) {
956         case CONNMAN_SERVICE_STATE_UNKNOWN:
957         case CONNMAN_SERVICE_STATE_IDLE:
958                 break;
959         case CONNMAN_SERVICE_STATE_ASSOCIATION:
960         case CONNMAN_SERVICE_STATE_CONFIGURATION:
961                 return TRUE;
962         case CONNMAN_SERVICE_STATE_DISCONNECT:
963         case CONNMAN_SERVICE_STATE_FAILURE:
964         case CONNMAN_SERVICE_STATE_READY:
965         case CONNMAN_SERVICE_STATE_ONLINE:
966                 break;
967         }
968
969         return FALSE;
970 }
971
972 static connman_bool_t explicit_connect(enum connman_session_reason reason)
973 {
974         switch (reason) {
975         case CONNMAN_SESSION_REASON_UNKNOWN:
976         case CONNMAN_SESSION_REASON_FREE_RIDE:
977                 break;
978         case CONNMAN_SESSION_REASON_CONNECT:
979                 return TRUE;
980         }
981
982         return FALSE;
983 }
984
985 static connman_bool_t explicit_disconnect(struct session_info *info)
986 {
987         if (info->entry == NULL)
988                 return FALSE;
989
990         DBG("reason %s service %p state %d",
991                 reason2string(info->entry->reason),
992                 info->entry->service, info->entry->state);
993
994         if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
995                 return FALSE;
996
997         if (explicit_connect(info->entry->reason) == FALSE)
998                 return FALSE;
999
1000         if (__connman_service_session_dec(info->entry->service) == FALSE)
1001                 return FALSE;
1002
1003         return TRUE;
1004 }
1005
1006 struct pending_data {
1007         unsigned int timeout;
1008         struct service_entry *entry;
1009         gboolean (*cb)(gpointer);
1010 };
1011
1012 static void pending_timeout_free(gpointer data, gpointer user_data)
1013 {
1014         struct pending_data *pending = data;
1015
1016         DBG("pending %p timeout %d", pending, pending->timeout);
1017         g_source_remove(pending->timeout);
1018         g_free(pending);
1019 }
1020
1021 static void pending_timeout_remove_all(struct service_entry *entry)
1022 {
1023         DBG("");
1024
1025         g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
1026         g_slist_free(entry->pending_timeouts);
1027         entry->pending_timeouts = NULL;
1028 }
1029
1030 static gboolean pending_timeout_cb(gpointer data)
1031 {
1032         struct pending_data *pending = data;
1033         struct service_entry *entry = pending->entry;
1034         gboolean ret;
1035
1036         DBG("pending %p timeout %d", pending, pending->timeout);
1037
1038         ret = pending->cb(pending->entry);
1039         if (ret == FALSE) {
1040                 entry->pending_timeouts =
1041                         g_slist_remove(entry->pending_timeouts,
1042                                         pending);
1043                 g_free(pending);
1044         }
1045         return ret;
1046 }
1047
1048 static connman_bool_t pending_timeout_add(unsigned int seconds,
1049                                         gboolean (*cb)(gpointer),
1050                                         struct service_entry *entry)
1051 {
1052         struct pending_data *pending = g_try_new0(struct pending_data, 1);
1053
1054         if (pending == NULL || cb == NULL || entry == NULL) {
1055                 g_free(pending);
1056                 return FALSE;
1057         }
1058
1059         pending->cb = cb;
1060         pending->entry = entry;
1061         pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
1062                                                 pending);
1063         entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
1064                                                 pending);
1065
1066         DBG("pending %p entry %p timeout id %d", pending, entry,
1067                 pending->timeout);
1068
1069         return TRUE;
1070 }
1071
1072 static gboolean call_disconnect(gpointer user_data)
1073 {
1074         struct service_entry *entry = user_data;
1075         struct connman_service *service = entry->service;
1076
1077         /*
1078          * TODO: We should mark this entry as pending work. In case
1079          * disconnect fails we just unassign this session from the
1080          * service and can't do anything later on it
1081          */
1082         DBG("disconnect service %p", service);
1083         __connman_service_disconnect(service);
1084
1085         return FALSE;
1086 }
1087
1088 static gboolean call_connect(gpointer user_data)
1089 {
1090         struct service_entry *entry = user_data;
1091         struct connman_service *service = entry->service;
1092
1093         DBG("connect service %p", service);
1094         __connman_service_connect(service);
1095
1096         return FALSE;
1097 }
1098
1099 static void deselect_service(struct session_info *info)
1100 {
1101         struct service_entry *entry;
1102         connman_bool_t disconnect, connected;
1103
1104         DBG("");
1105
1106         if (info->entry == NULL)
1107                 return;
1108
1109         disconnect = explicit_disconnect(info);
1110
1111         connected = is_connecting(info->entry->state) == TRUE ||
1112                         is_connected(info->entry->state) == TRUE;
1113
1114         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1115         info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1116
1117         entry = info->entry;
1118         info->entry = NULL;
1119
1120         DBG("disconnect %d connected %d", disconnect, connected);
1121
1122         if (disconnect == TRUE && connected == TRUE)
1123                 pending_timeout_add(0, call_disconnect, entry);
1124 }
1125
1126 static void deselect_and_disconnect(struct connman_session *session)
1127 {
1128         struct session_info *info = session->info;
1129
1130         deselect_service(info);
1131
1132         info->reason = CONNMAN_SESSION_REASON_FREE_RIDE;
1133 }
1134
1135 static void select_connected_service(struct session_info *info,
1136                                         struct service_entry *entry)
1137 {
1138         enum connman_session_state state;
1139
1140         state = service_to_session_state(entry->state);
1141         if (is_type_matching_state(&state, info->config.type) == FALSE)
1142                 return;
1143
1144         info->state = state;
1145
1146         info->entry = entry;
1147         info->entry->reason = info->reason;
1148
1149         if (explicit_connect(info->reason) == FALSE)
1150                 return;
1151
1152         __connman_service_session_inc(info->entry->service);
1153 }
1154
1155 static void select_offline_service(struct session_info *info,
1156                                         struct service_entry *entry)
1157 {
1158         if (explicit_connect(info->reason) == FALSE)
1159                 return;
1160
1161         info->state = service_to_session_state(entry->state);
1162
1163         info->entry = entry;
1164         info->entry->reason = info->reason;
1165
1166         __connman_service_session_inc(info->entry->service);
1167         pending_timeout_add(0, call_connect, entry);
1168 }
1169
1170 static void select_service(struct session_info *info,
1171                                 struct service_entry *entry)
1172 {
1173         DBG("service %p", entry->service);
1174
1175         if (is_connected(entry->state) == TRUE)
1176                 select_connected_service(info, entry);
1177         else
1178                 select_offline_service(info, entry);
1179 }
1180
1181 static void select_and_connect(struct connman_session *session,
1182                                 enum connman_session_reason reason)
1183 {
1184         struct session_info *info = session->info;
1185         struct service_entry *entry = NULL;
1186         GList *list;
1187
1188         DBG("session %p reason %s", session, reason2string(reason));
1189
1190         info->reason = reason;
1191
1192         for (list = session->service_list; list != NULL; list = list->next) {
1193                 entry = list->data;
1194
1195                 switch (entry->state) {
1196                 case CONNMAN_SERVICE_STATE_ASSOCIATION:
1197                 case CONNMAN_SERVICE_STATE_CONFIGURATION:
1198                 case CONNMAN_SERVICE_STATE_READY:
1199                 case CONNMAN_SERVICE_STATE_ONLINE:
1200                 case CONNMAN_SERVICE_STATE_IDLE:
1201                 case CONNMAN_SERVICE_STATE_DISCONNECT:
1202                         select_service(info, entry);
1203                         return;
1204                 case CONNMAN_SERVICE_STATE_UNKNOWN:
1205                 case CONNMAN_SERVICE_STATE_FAILURE:
1206                         break;
1207                 }
1208         }
1209 }
1210
1211 static struct service_entry *create_service_entry(struct connman_session * session,
1212                                         struct connman_service *service,
1213                                         const char *name,
1214                                         enum connman_service_state state)
1215 {
1216         struct service_entry *entry;
1217         enum connman_service_type type;
1218         int idx;
1219
1220         entry = g_try_new0(struct service_entry, 1);
1221         if (entry == NULL)
1222                 return entry;
1223
1224         entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1225         entry->state = state;
1226         if (name != NULL)
1227                 entry->name = name;
1228         else
1229                 entry->name = "";
1230         entry->service = service;
1231
1232         idx = __connman_service_get_index(entry->service);
1233         entry->ifname = connman_inet_ifname(idx);
1234         if (entry->ifname == NULL)
1235                 entry->ifname = g_strdup("");
1236
1237         type = connman_service_get_type(entry->service);
1238         entry->bearer = service2bearer(type);
1239
1240         entry->session = session;
1241
1242         return entry;
1243 }
1244
1245 static void iterate_service_cb(struct connman_service *service,
1246                                 const char *name,
1247                                 enum connman_service_state state,
1248                                 void *user_data)
1249 {
1250         struct connman_session *session = user_data;
1251         struct service_entry *entry;
1252
1253         if (service_match(session, service) == FALSE)
1254                 return;
1255
1256         entry = create_service_entry(session, service, name, state);
1257         if (entry == NULL)
1258                 return;
1259
1260         g_hash_table_replace(session->service_hash, service, entry);
1261 }
1262
1263 static void destroy_service_entry(gpointer data)
1264 {
1265         struct service_entry *entry = data;
1266         struct session_info *info = entry->session->info;
1267         struct connman_session *session;
1268
1269         if (info != NULL && info->entry == entry) {
1270                 session = entry->session;
1271                 deselect_and_disconnect(session);
1272         }
1273
1274         pending_timeout_remove_all(entry);
1275         g_free(entry->ifname);
1276
1277         g_free(entry);
1278 }
1279
1280 static void populate_service_list(struct connman_session *session)
1281 {
1282         GHashTableIter iter;
1283         gpointer key, value;
1284
1285         session->service_hash =
1286                 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1287                                         NULL, destroy_service_entry);
1288         __connman_service_iterate_services(iterate_service_cb, session);
1289
1290         g_hash_table_iter_init(&iter, session->service_hash);
1291
1292         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1293                 struct service_entry *entry = value;
1294
1295                 DBG("service %p type %s name %s", entry->service,
1296                         service2bearer(connman_service_get_type(entry->service)),
1297                         entry->name);
1298                 session->service_list = g_list_prepend(session->service_list,
1299                                                         entry);
1300         }
1301
1302         session->service_list = g_list_sort_with_data(session->service_list,
1303                                                 sort_services, session);
1304 }
1305
1306 static void session_changed(struct connman_session *session,
1307                                 enum connman_session_trigger trigger)
1308 {
1309         struct session_info *info = session->info;
1310         struct session_info *info_last = session->info_last;
1311         struct connman_service *service;
1312         struct service_entry *entry = NULL, *entry_last = NULL;
1313         GHashTable *service_hash_last;
1314
1315         /*
1316          * TODO: This only a placeholder for the 'real' algorithm to
1317          * play a bit around. So we are going to improve it step by step.
1318          */
1319
1320         DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1321                                                 reason2string(info->reason));
1322
1323         if (info->entry != NULL) {
1324                 enum connman_session_state state;
1325
1326                 state = service_to_session_state(info->entry->state);
1327
1328                 if (is_type_matching_state(&state, info->config.type) == TRUE)
1329                         info->state = state;
1330         }
1331
1332         switch (trigger) {
1333         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1334                 DBG("ignore session changed event");
1335                 return;
1336         case CONNMAN_SESSION_TRIGGER_SETTING:
1337                 if (info->config.allowed_bearers != info_last->config.allowed_bearers) {
1338
1339                         service_hash_last = session->service_hash;
1340                         g_list_free(session->service_list);
1341                         session->service_list = NULL;
1342
1343                         if (info->entry != NULL)
1344                                 service = info->entry->service;
1345                         else
1346                                 service = NULL;
1347
1348                         populate_service_list(session);
1349
1350                         if (info->entry != NULL) {
1351                                 entry_last = g_hash_table_lookup(
1352                                                         service_hash_last,
1353                                                         info->entry->service);
1354                                 entry = g_hash_table_lookup(
1355                                                         session->service_hash,
1356                                                         service);
1357                         }
1358
1359                         if (entry == NULL && entry_last != NULL) {
1360                                 /*
1361                                  * The currently selected service is
1362                                  * not part of this session anymore.
1363                                  */
1364                                 deselect_and_disconnect(session);
1365                         }
1366
1367                         g_hash_table_destroy(service_hash_last);
1368                 }
1369
1370                 if (info->config.type != info_last->config.type) {
1371                         if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
1372                                         is_type_matching_state(&info->state,
1373                                                         info->config.type) == FALSE)
1374                                 deselect_and_disconnect(session);
1375                 }
1376
1377                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1378                         select_and_connect(session,
1379                                         CONNMAN_SESSION_REASON_FREE_RIDE);
1380                 }
1381
1382                 break;
1383         case CONNMAN_SESSION_TRIGGER_ECALL:
1384                 /*
1385                  * For the time beeing we fallback to normal connect
1386                  * strategy.
1387                  */
1388         case CONNMAN_SESSION_TRIGGER_CONNECT:
1389                 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1390                         if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1391                                 break;
1392                         info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1393                         __connman_service_session_inc(info->entry->service);
1394                         break;
1395                 }
1396
1397                 if (info->entry != NULL &&
1398                                 is_connecting(info->entry->state) == TRUE) {
1399                         break;
1400                 }
1401
1402                 select_and_connect(session,
1403                                 CONNMAN_SESSION_REASON_CONNECT);
1404
1405                 break;
1406         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1407                 deselect_and_disconnect(session);
1408
1409                 break;
1410         case CONNMAN_SESSION_TRIGGER_SERVICE:
1411                 if (info->entry != NULL &&
1412                         (is_connecting(info->entry->state) == TRUE ||
1413                                 is_connected(info->entry->state) == TRUE)) {
1414                         break;
1415                 }
1416
1417                 deselect_and_disconnect(session);
1418
1419                 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) {
1420                         select_and_connect(session, info->reason);
1421                 }
1422
1423                 break;
1424         }
1425
1426         session_notify(session);
1427 }
1428
1429 int connman_session_config_update(struct connman_session *session)
1430 {
1431         struct session_info *info = session->info;
1432         GSList *allowed_bearers;
1433         int err;
1434
1435         DBG("session %p", session);
1436
1437         /*
1438          * We update all configuration even though only one entry
1439          * might have changed. We can still optimize this later.
1440          */
1441
1442         err = apply_policy_on_bearers(
1443                 session->policy_config->allowed_bearers,
1444                 session->user_allowed_bearers,
1445                 &allowed_bearers);
1446         if (err < 0)
1447                 return err;
1448
1449         g_slist_free(info->config.allowed_bearers);
1450         info->config.allowed_bearers = allowed_bearers;
1451
1452         info->config.type = apply_policy_on_type(
1453                                 session->policy_config->type,
1454                                 info->config.type);
1455
1456         info->config.roaming_policy = session->policy_config->roaming_policy;
1457
1458         info->config.ecall = session->policy_config->ecall;
1459         if (info->config.ecall == TRUE)
1460                 ecall_session = session;
1461
1462         info->config.priority = session->policy_config->priority;
1463
1464         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1465
1466         return 0;
1467 }
1468
1469 static DBusMessage *connect_session(DBusConnection *conn,
1470                                         DBusMessage *msg, void *user_data)
1471 {
1472         struct connman_session *session = user_data;
1473
1474         DBG("session %p", session);
1475
1476         if (ecall_session != NULL) {
1477                 if (ecall_session->ecall == TRUE && ecall_session != session)
1478                         return __connman_error_failed(msg, EBUSY);
1479
1480                 session->ecall = TRUE;
1481                 session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL);
1482         } else
1483                 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1484
1485         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1486 }
1487
1488 static DBusMessage *disconnect_session(DBusConnection *conn,
1489                                         DBusMessage *msg, void *user_data)
1490 {
1491         struct connman_session *session = user_data;
1492
1493         DBG("session %p", session);
1494
1495         if (ecall_session != NULL) {
1496                 if (ecall_session->ecall == TRUE && ecall_session != session)
1497                         return __connman_error_failed(msg, EBUSY);
1498
1499                 session->ecall = FALSE;
1500         }
1501
1502         session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1503
1504         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1505 }
1506
1507 static DBusMessage *change_session(DBusConnection *conn,
1508                                         DBusMessage *msg, void *user_data)
1509 {
1510         struct connman_session *session = user_data;
1511         struct session_info *info = session->info;
1512         DBusMessageIter iter, value;
1513         const char *name;
1514         const char *val;
1515         GSList *allowed_bearers;
1516         int err;
1517
1518         DBG("session %p", session);
1519         if (dbus_message_iter_init(msg, &iter) == FALSE)
1520                 return __connman_error_invalid_arguments(msg);
1521
1522         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1523                 return __connman_error_invalid_arguments(msg);
1524
1525         dbus_message_iter_get_basic(&iter, &name);
1526         dbus_message_iter_next(&iter);
1527
1528         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1529                 return __connman_error_invalid_arguments(msg);
1530
1531         dbus_message_iter_recurse(&iter, &value);
1532
1533         switch (dbus_message_iter_get_arg_type(&value)) {
1534         case DBUS_TYPE_ARRAY:
1535                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1536                         err = parse_bearers(&value, &allowed_bearers);
1537                         if (err < 0)
1538                                 return __connman_error_failed(msg, err);
1539
1540                         g_slist_free(info->config.allowed_bearers);
1541                         session->user_allowed_bearers = allowed_bearers;
1542
1543                         err = apply_policy_on_bearers(
1544                                         session->policy_config->allowed_bearers,
1545                                         session->user_allowed_bearers,
1546                                         &info->config.allowed_bearers);
1547
1548                         if (err < 0)
1549                                 return __connman_error_failed(msg, err);
1550                 } else {
1551                         goto err;
1552                 }
1553                 break;
1554         case DBUS_TYPE_STRING:
1555                 if (g_str_equal(name, "ConnectionType") == TRUE) {
1556                         dbus_message_iter_get_basic(&value, &val);
1557                         info->config.type = apply_policy_on_type(
1558                                 session->policy_config->type,
1559                                 connman_session_parse_connection_type(val));
1560                 } else {
1561                         goto err;
1562                 }
1563                 break;
1564         default:
1565                 goto err;
1566         }
1567
1568         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1569
1570         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1571
1572 err:
1573         return __connman_error_invalid_arguments(msg);
1574 }
1575
1576 static void release_session(gpointer key, gpointer value, gpointer user_data)
1577 {
1578         struct connman_session *session = value;
1579         DBusMessage *message;
1580
1581         DBG("owner %s path %s", session->owner, session->notify_path);
1582
1583         if (session->notify_watch > 0)
1584                 g_dbus_remove_watch(connection, session->notify_watch);
1585
1586         g_dbus_unregister_interface(connection, session->session_path,
1587                                                 CONNMAN_SESSION_INTERFACE);
1588
1589         message = dbus_message_new_method_call(session->owner,
1590                                                 session->notify_path,
1591                                                 CONNMAN_NOTIFICATION_INTERFACE,
1592                                                 "Release");
1593         if (message == NULL)
1594                 return;
1595
1596         dbus_message_set_no_reply(message, TRUE);
1597
1598         g_dbus_send_message(connection, message);
1599 }
1600
1601 static int session_disconnect(struct connman_session *session)
1602 {
1603         DBG("session %p, %s", session, session->owner);
1604
1605         if (session->notify_watch > 0)
1606                 g_dbus_remove_watch(connection, session->notify_watch);
1607
1608         g_dbus_unregister_interface(connection, session->session_path,
1609                                                 CONNMAN_SESSION_INTERFACE);
1610
1611         deselect_and_disconnect(session);
1612
1613         g_hash_table_remove(session_hash, session->session_path);
1614
1615         return 0;
1616 }
1617
1618 static void owner_disconnect(DBusConnection *conn, void *user_data)
1619 {
1620         struct connman_session *session = user_data;
1621
1622         DBG("session %p, %s died", session, session->owner);
1623
1624         session_disconnect(session);
1625 }
1626
1627 static DBusMessage *destroy_session(DBusConnection *conn,
1628                                         DBusMessage *msg, void *user_data)
1629 {
1630         struct connman_session *session = user_data;
1631
1632         DBG("session %p", session);
1633
1634         if (ecall_session != NULL && ecall_session != session)
1635                 return __connman_error_failed(msg, EBUSY);
1636
1637         session_disconnect(session);
1638
1639         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1640 }
1641
1642 static const GDBusMethodTable session_methods[] = {
1643         { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
1644         { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
1645         { GDBUS_METHOD("Disconnect", NULL, NULL,
1646                         disconnect_session ) },
1647         { GDBUS_METHOD("Change",
1648                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1649                         NULL, change_session) },
1650         { },
1651 };
1652
1653 static int session_create_cb(struct connman_session *session,
1654                                 struct connman_session_config *config,
1655                                 void *user_data, int err)
1656 {
1657         DBusMessage *reply;
1658         struct user_config *user_config = user_data;
1659         struct session_info *info, *info_last;
1660
1661         DBG("session %p config %p", session, config);
1662
1663         if (err != 0)
1664                 goto out;
1665
1666         session->policy_config = config;
1667
1668         info = session->info;
1669         info_last = session->info_last;
1670
1671         if (session->policy_config->ecall == TRUE)
1672                 ecall_session = session;
1673
1674         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1675         info->config.type = apply_policy_on_type(
1676                                 session->policy_config->type,
1677                                 user_config->type);
1678         info->config.priority = session->policy_config->priority;
1679         info->config.roaming_policy = session->policy_config->roaming_policy;
1680         info->entry = NULL;
1681
1682         session->user_allowed_bearers = user_config->allowed_bearers;
1683         user_config->allowed_bearers = NULL;
1684
1685         err = apply_policy_on_bearers(
1686                         session->policy_config->allowed_bearers,
1687                         session->user_allowed_bearers,
1688                         &info->config.allowed_bearers);
1689         if (err < 0)
1690                 goto out;
1691
1692         g_hash_table_replace(session_hash, session->session_path, session);
1693
1694         DBG("add %s", session->session_path);
1695
1696         if (g_dbus_register_interface(connection, session->session_path,
1697                                         CONNMAN_SESSION_INTERFACE,
1698                                         session_methods, NULL,
1699                                         NULL, session, NULL) == FALSE) {
1700                 connman_error("Failed to register %s", session->session_path);
1701                 g_hash_table_remove(session_hash, session->session_path);
1702                 err = -EINVAL;
1703                 goto out;
1704         }
1705
1706         reply = g_dbus_create_reply(user_config->pending,
1707                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1708                                 DBUS_TYPE_INVALID);
1709         g_dbus_send_message(connection, reply);
1710         user_config->pending = NULL;
1711
1712         populate_service_list(session);
1713
1714         info_last->state = info->state;
1715         info_last->config.priority = info->config.priority;
1716         info_last->config.roaming_policy = info->config.roaming_policy;
1717         info_last->entry = info->entry;
1718         info_last->config.allowed_bearers = info->config.allowed_bearers;
1719
1720         session->append_all = TRUE;
1721
1722         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1723
1724 out:
1725         if (err < 0) {
1726                 reply = __connman_error_failed(user_config->pending, -err);
1727                 g_dbus_send_message(connection, reply);
1728
1729                 free_session(session);
1730         }
1731
1732         cleanup_user_config(user_config);
1733
1734         return err;
1735 }
1736
1737 int __connman_session_create(DBusMessage *msg)
1738 {
1739         const char *owner, *notify_path;
1740         char *session_path = NULL;
1741         DBusMessageIter iter, array;
1742         struct connman_session *session = NULL;
1743         struct user_config *user_config = NULL;
1744         connman_bool_t user_allowed_bearers = FALSE;
1745         connman_bool_t user_connection_type = FALSE;
1746         int err;
1747
1748         owner = dbus_message_get_sender(msg);
1749
1750         DBG("owner %s", owner);
1751
1752         if (ecall_session != NULL && ecall_session->ecall == TRUE) {
1753                 /*
1754                  * If there is an emergency call already going on,
1755                  * ignore session creation attempt
1756                  */
1757                 err = -EBUSY;
1758                 goto err;
1759         }
1760
1761         user_config = g_try_new0(struct user_config, 1);
1762         if (user_config == NULL) {
1763                 err = -ENOMEM;
1764                 goto err;
1765         }
1766
1767         user_config->pending = dbus_message_ref(msg);
1768
1769         dbus_message_iter_init(msg, &iter);
1770         dbus_message_iter_recurse(&iter, &array);
1771
1772         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1773                 DBusMessageIter entry, value;
1774                 const char *key, *val;
1775
1776                 dbus_message_iter_recurse(&array, &entry);
1777                 dbus_message_iter_get_basic(&entry, &key);
1778
1779                 dbus_message_iter_next(&entry);
1780                 dbus_message_iter_recurse(&entry, &value);
1781
1782                 switch (dbus_message_iter_get_arg_type(&value)) {
1783                 case DBUS_TYPE_ARRAY:
1784                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1785                                 err = parse_bearers(&value,
1786                                                 &user_config->allowed_bearers);
1787                                 if (err < 0)
1788                                         goto err;
1789
1790                                 user_allowed_bearers = TRUE;
1791                         } else {
1792                                 err = -EINVAL;
1793                                 goto err;
1794                         }
1795                         break;
1796                 case DBUS_TYPE_STRING:
1797                         if (g_str_equal(key, "ConnectionType") == TRUE) {
1798                                 dbus_message_iter_get_basic(&value, &val);
1799                                 user_config->type =
1800                                         connman_session_parse_connection_type(val);
1801
1802                                 user_connection_type = TRUE;
1803                         } else {
1804                                 err = -EINVAL;
1805                                 goto err;
1806                         }
1807                 }
1808                 dbus_message_iter_next(&array);
1809         }
1810
1811         /*
1812          * If the user hasn't provided a configuration, we set
1813          * the default configuration.
1814          *
1815          * For AllowedBearers this is '*', ...
1816          */
1817         if (user_allowed_bearers == FALSE) {
1818                 user_config->allowed_bearers =
1819                         g_slist_append(NULL,
1820                                 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
1821                 if (user_config->allowed_bearers == NULL) {
1822                         err = -ENOMEM;
1823                         goto err;
1824                 }
1825         }
1826
1827         /* ... and for ConnectionType it is 'any'. */
1828         if (user_connection_type == FALSE)
1829                 user_config->type = CONNMAN_SESSION_TYPE_ANY;
1830
1831         dbus_message_iter_next(&iter);
1832         dbus_message_iter_get_basic(&iter, &notify_path);
1833
1834         if (notify_path == NULL) {
1835                 err = -EINVAL;
1836                 goto err;
1837         }
1838
1839         session_path = g_strdup_printf("/sessions%s", notify_path);
1840         if (session_path == NULL) {
1841                 err = -ENOMEM;
1842                 goto err;
1843         }
1844
1845         session = g_hash_table_lookup(session_hash, session_path);
1846         if (session != NULL) {
1847                 g_free(session_path);
1848                 session = NULL;
1849                 err = -EEXIST;
1850                 goto err;
1851         }
1852
1853         session = g_try_new0(struct connman_session, 1);
1854         if (session == NULL) {
1855                 g_free(session_path);
1856                 err = -ENOMEM;
1857                 goto err;
1858         }
1859
1860         session->session_path = session_path;
1861
1862         session->info = g_try_new0(struct session_info, 1);
1863         if (session->info == NULL) {
1864                 err = -ENOMEM;
1865                 goto err;
1866         }
1867
1868         session->info_last = g_try_new0(struct session_info, 1);
1869         if (session->info_last == NULL) {
1870                 err = -ENOMEM;
1871                 goto err;
1872         }
1873
1874         session->owner = g_strdup(owner);
1875         session->notify_path = g_strdup(notify_path);
1876         session->notify_watch =
1877                 g_dbus_add_disconnect_watch(connection, session->owner,
1878                                         owner_disconnect, session, NULL);
1879
1880         err = assign_policy_plugin(session);
1881         if (err < 0)
1882                 goto err;
1883
1884         err = create_policy_config(session, session_create_cb, user_config);
1885         if (err < 0 && err != -EINPROGRESS)
1886                 return err;
1887
1888         return -EINPROGRESS;
1889
1890 err:
1891         connman_error("Failed to create session");
1892
1893         free_session(session);
1894
1895         cleanup_user_config(user_config);
1896         return err;
1897 }
1898
1899 void connman_session_destroy(struct connman_session *session)
1900 {
1901         DBG("session %p", session);
1902
1903         session_disconnect(session);
1904 }
1905
1906 int __connman_session_destroy(DBusMessage *msg)
1907 {
1908         const char *owner, *session_path;
1909         struct connman_session *session;
1910
1911         owner = dbus_message_get_sender(msg);
1912
1913         DBG("owner %s", owner);
1914
1915         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1916                                                         DBUS_TYPE_INVALID);
1917         if (session_path == NULL)
1918                 return -EINVAL;
1919
1920         session = g_hash_table_lookup(session_hash, session_path);
1921         if (session == NULL)
1922                 return -EINVAL;
1923
1924         if (g_strcmp0(owner, session->owner) != 0)
1925                 return -EACCES;
1926
1927         connman_session_destroy(session);
1928
1929         return 0;
1930 }
1931
1932 connman_bool_t __connman_session_mode()
1933 {
1934         return sessionmode;
1935 }
1936
1937 void __connman_session_set_mode(connman_bool_t enable)
1938 {
1939         DBG("enable %d", enable);
1940
1941         if (sessionmode != enable) {
1942                 sessionmode = enable;
1943
1944                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1945                                 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1946                                 DBUS_TYPE_BOOLEAN, &sessionmode);
1947         }
1948
1949         if (sessionmode == TRUE)
1950                 __connman_service_disconnect_all();
1951 }
1952
1953 static void service_add(struct connman_service *service,
1954                         const char *name)
1955 {
1956         GHashTableIter iter;
1957         gpointer key, value;
1958         struct connman_session *session;
1959         struct service_entry *entry;
1960
1961         DBG("service %p", service);
1962
1963         g_hash_table_iter_init(&iter, session_hash);
1964
1965         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1966                 session = value;
1967
1968                 if (service_match(session, service) == FALSE)
1969                         continue;
1970
1971                 entry = create_service_entry(session, service, name,
1972                                                 CONNMAN_SERVICE_STATE_IDLE);
1973                 if (entry == NULL)
1974                         continue;
1975
1976                 session->service_list = g_list_insert_sorted_with_data(
1977                         session->service_list, entry, sort_services, session);
1978
1979                 g_hash_table_replace(session->service_hash, service, entry);
1980
1981                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1982         }
1983 }
1984
1985 static void service_remove(struct connman_service *service)
1986 {
1987
1988         GHashTableIter iter;
1989         gpointer key, value;
1990         struct connman_session *session;
1991         struct session_info *info;
1992
1993         DBG("service %p", service);
1994
1995         g_hash_table_iter_init(&iter, session_hash);
1996
1997         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1998                 struct service_entry *entry;
1999                 session = value;
2000                 info = session->info;
2001
2002                 entry = g_hash_table_lookup(session->service_hash, service);
2003                 if (entry == NULL)
2004                         continue;
2005
2006                 session->service_list = g_list_remove(session->service_list,
2007                                                         entry);
2008
2009                 g_hash_table_remove(session->service_hash, service);
2010
2011                 if (info->entry != NULL && info->entry->service == service)
2012                         info->entry = NULL;
2013                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
2014         }
2015 }
2016
2017 static void service_state_changed(struct connman_service *service,
2018                                         enum connman_service_state state)
2019 {
2020         GHashTableIter iter;
2021         gpointer key, value;
2022
2023         DBG("service %p state %d", service, state);
2024
2025         g_hash_table_iter_init(&iter, session_hash);
2026
2027         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
2028                 struct connman_session *session = value;
2029                 struct service_entry *entry;
2030
2031                 entry = g_hash_table_lookup(session->service_hash, service);
2032                 if (entry != NULL)
2033                         entry->state = state;
2034
2035                 session_changed(session,
2036                                 CONNMAN_SESSION_TRIGGER_SERVICE);
2037         }
2038 }
2039
2040 static void ipconfig_changed(struct connman_service *service,
2041                                 struct connman_ipconfig *ipconfig)
2042 {
2043         GHashTableIter iter;
2044         gpointer key, value;
2045         struct connman_session *session;
2046         struct session_info *info;
2047         enum connman_ipconfig_type type;
2048
2049         DBG("service %p ipconfig %p", service, ipconfig);
2050
2051         type = __connman_ipconfig_get_config_type(ipconfig);
2052
2053         g_hash_table_iter_init(&iter, session_hash);
2054
2055         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
2056                 session = value;
2057                 info = session->info;
2058
2059                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
2060                         continue;
2061
2062                 if (info->entry != NULL && info->entry->service == service) {
2063                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
2064                                 ipconfig_ipv4_changed(session);
2065                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
2066                                 ipconfig_ipv6_changed(session);
2067                 }
2068         }
2069 }
2070
2071 static struct connman_notifier session_notifier = {
2072         .name                   = "session",
2073         .service_add            = service_add,
2074         .service_remove         = service_remove,
2075         .service_state_changed  = service_state_changed,
2076         .ipconfig_changed       = ipconfig_changed,
2077 };
2078
2079 int __connman_session_init(void)
2080 {
2081         int err;
2082
2083         DBG("");
2084
2085         connection = connman_dbus_get_connection();
2086         if (connection == NULL)
2087                 return -1;
2088
2089         err = connman_notifier_register(&session_notifier);
2090         if (err < 0) {
2091                 dbus_connection_unref(connection);
2092                 return err;
2093         }
2094
2095         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2096                                                 NULL, cleanup_session);
2097
2098         sessionmode = FALSE;
2099         return 0;
2100 }
2101
2102 void __connman_session_cleanup(void)
2103 {
2104         DBG("");
2105
2106         if (connection == NULL)
2107                 return;
2108
2109         connman_notifier_unregister(&session_notifier);
2110
2111         g_hash_table_foreach(session_hash, release_session, NULL);
2112         g_hash_table_destroy(session_hash);
2113         session_hash = NULL;
2114
2115         dbus_connection_unref(connection);
2116 }