X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fsession.c;h=6000b6d9f130984088b5e4eb344b453c4f79aac1;hb=7ef7e96fc2f3eb620fffb6f277339214efe83747;hp=076eb8097e4db8214eb02451a472ede2027aa981;hpb=8008dace850e867ce39b120fc9ba5ffd860e5c94;p=platform%2Fupstream%2Fconnman.git diff --git a/src/session.c b/src/session.c index 076eb80..6000b6d 100644 --- a/src/session.c +++ b/src/session.c @@ -2,8 +2,8 @@ * * Connection Manager * - * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved. + * Copyright (C) 2007-2014 Intel Corporation. All rights reserved. + * Copyright (C) 2011-2014 BMW Car IT GmbH. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -28,53 +28,17 @@ #include -#include - #include "connman.h" static DBusConnection *connection; static GHashTable *session_hash; -static connman_bool_t sessionmode; +static GHashTable *service_hash; static struct connman_session *ecall_session; -static GSList *policy_list; - -enum connman_session_trigger { - CONNMAN_SESSION_TRIGGER_UNKNOWN = 0, - CONNMAN_SESSION_TRIGGER_SETTING = 1, - CONNMAN_SESSION_TRIGGER_CONNECT = 2, - CONNMAN_SESSION_TRIGGER_DISCONNECT = 3, - CONNMAN_SESSION_TRIGGER_SERVICE = 4, - CONNMAN_SESSION_TRIGGER_ECALL = 5, -}; - -enum connman_session_reason { - CONNMAN_SESSION_REASON_UNKNOWN = 0, - CONNMAN_SESSION_REASON_CONNECT = 1, - CONNMAN_SESSION_REASON_FREE_RIDE = 2, -}; - -enum connman_session_state { - CONNMAN_SESSION_STATE_DISCONNECTED = 0, - CONNMAN_SESSION_STATE_CONNECTED = 1, - CONNMAN_SESSION_STATE_ONLINE = 2, -}; - -struct service_entry { - /* track why this service was selected */ - enum connman_session_reason reason; - enum connman_service_state state; - const char *name; - struct connman_service *service; - char *ifname; - const char *bearer; - GSList *pending_timeouts; -}; +static uint32_t session_mark = 256; struct session_info { struct connman_session_config config; enum connman_session_state state; - struct service_entry *entry; - enum connman_session_reason reason; }; struct connman_session { @@ -83,52 +47,55 @@ struct connman_session { char *notify_path; guint notify_watch; - struct connman_session_policy *policy; - - connman_bool_t append_all; + bool active; + bool append_all; struct session_info *info; struct session_info *info_last; + struct connman_service *service; + struct connman_service *service_last; struct connman_session_config *policy_config; GSList *user_allowed_bearers; + char *user_allowed_interface; + + bool ecall; + + enum connman_session_id_type id_type; + struct firewall_context *fw; + uint32_t mark; + int index; + char *addr; + char *gateway; + unsigned char prefixlen; + bool policy_routing; + bool snat_enabled; +}; - connman_bool_t ecall; +struct connman_service_info { + struct connman_service *service; + GSList *sessions; +}; - GSequence *service_list; - GHashTable *service_hash; +struct fw_snat { + GSList *sessions; + int id; + int index; + char *addr; + struct firewall_context *fw; }; -static const char *trigger2string(enum connman_session_trigger trigger) -{ - switch (trigger) { - case CONNMAN_SESSION_TRIGGER_UNKNOWN: - break; - case CONNMAN_SESSION_TRIGGER_SETTING: - return "setting"; - case CONNMAN_SESSION_TRIGGER_CONNECT: - return "connect"; - case CONNMAN_SESSION_TRIGGER_DISCONNECT: - return "disconnect"; - case CONNMAN_SESSION_TRIGGER_SERVICE: - return "service"; - case CONNMAN_SESSION_TRIGGER_ECALL: - return "ecall"; - } +GSList *fw_snat_list; - return NULL; -} +static struct connman_session_policy *policy; +static void session_activate(struct connman_session *session); +static void session_deactivate(struct connman_session *session); +static void update_session_state(struct connman_session *session); -static const char *reason2string(enum connman_session_reason reason) +static void cleanup_service(gpointer data) { - switch (reason) { - case CONNMAN_SESSION_REASON_UNKNOWN: - return "unknown"; - case CONNMAN_SESSION_REASON_CONNECT: - return "connect"; - case CONNMAN_SESSION_REASON_FREE_RIDE: - return "free-ride"; - } + struct connman_service_info *info = data; - return NULL; + g_slist_free(info->sessions); + g_free(info); } static const char *state2string(enum connman_session_state state) @@ -195,16 +162,14 @@ static int bearer2service(const char *bearer, enum connman_service_type *type) *type = CONNMAN_SERVICE_TYPE_ETHERNET; else if (g_strcmp0(bearer, "wifi") == 0) *type = CONNMAN_SERVICE_TYPE_WIFI; - else if (g_strcmp0(bearer, "wimax") == 0) - *type = CONNMAN_SERVICE_TYPE_WIMAX; + else if (g_strcmp0(bearer, "gadget") == 0) + *type = CONNMAN_SERVICE_TYPE_GADGET; else if (g_strcmp0(bearer, "bluetooth") == 0) *type = CONNMAN_SERVICE_TYPE_BLUETOOTH; else if (g_strcmp0(bearer, "cellular") == 0) *type = CONNMAN_SERVICE_TYPE_CELLULAR; else if (g_strcmp0(bearer, "vpn") == 0) *type = CONNMAN_SERVICE_TYPE_VPN; - else if (g_strcmp0(bearer, "*") == 0) - *type = CONNMAN_SERVICE_TYPE_UNKNOWN; else return -EINVAL; @@ -216,209 +181,502 @@ static char *service2bearer(enum connman_service_type type) switch (type) { case CONNMAN_SERVICE_TYPE_ETHERNET: return "ethernet"; + case CONNMAN_SERVICE_TYPE_GADGET: + return "gadget"; case CONNMAN_SERVICE_TYPE_WIFI: return "wifi"; - case CONNMAN_SERVICE_TYPE_WIMAX: - return "wimax"; case CONNMAN_SERVICE_TYPE_BLUETOOTH: return "bluetooth"; case CONNMAN_SERVICE_TYPE_CELLULAR: return "cellular"; case CONNMAN_SERVICE_TYPE_VPN: return "vpn"; - case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_GADGET: + case CONNMAN_SERVICE_TYPE_P2P: + case CONNMAN_SERVICE_TYPE_UNKNOWN: +#if defined TIZEN_EXT_WIFI_MESH + case CONNMAN_SERVICE_TYPE_MESH: +#endif return ""; } return ""; } -static void destroy_policy_config(struct connman_session *session) +static struct fw_snat *fw_snat_lookup(int index, const char *addr) { - if (session->policy == NULL) { - g_free(session->policy_config); - return; + struct fw_snat *fw_snat; + GSList *list; + + for (list = fw_snat_list; list; list = list->next) { + fw_snat = list->data; + + if (fw_snat->index == index) { + if (g_strcmp0(addr, fw_snat->addr) != 0) + continue; + return fw_snat; + } + } + return NULL; +} + +static int fw_snat_create(struct connman_session *session, + int index, const char *ifname, const char *addr) +{ + struct fw_snat *fw_snat; + int err; + + fw_snat = g_new0(struct fw_snat, 1); + + fw_snat->fw = __connman_firewall_create(); + fw_snat->index = index; + fw_snat->addr = g_strdup(addr); + + fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw, + index, ifname, addr); + if (fw_snat->id < 0) { + err = fw_snat->id; + goto err; } - (*session->policy->destroy)(session); + fw_snat_list = g_slist_prepend(fw_snat_list, fw_snat); + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); + + return 0; +err: + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat->addr); + g_free(fw_snat); + return err; } -static void free_session(struct connman_session *session) +static void fw_snat_ref(struct connman_session *session, + struct fw_snat *fw_snat) { - if (session == NULL) + if (g_slist_find(fw_snat->sessions, session)) return; + fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session); +} - if (session->notify_watch > 0) - g_dbus_remove_watch(connection, session->notify_watch); +static void fw_snat_unref(struct connman_session *session, + struct fw_snat *fw_snat) +{ + fw_snat->sessions = g_slist_remove(fw_snat->sessions, session); + if (fw_snat->sessions) + return; - destroy_policy_config(session); - g_slist_free(session->info->config.allowed_bearers); - g_free(session->owner); - g_free(session->session_path); - g_free(session->notify_path); - g_free(session->info); - g_free(session->info_last); + fw_snat_list = g_slist_remove(fw_snat_list, fw_snat); - g_free(session); + __connman_firewall_disable_snat(fw_snat->fw); + __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat); } -static void cleanup_session(gpointer user_data) +static int init_firewall_session(struct connman_session *session) { - struct connman_session *session = user_data; - struct session_info *info = session->info; + struct firewall_context *fw; + int err; + struct connman_ipconfig *ipconfig = NULL; + const char *addr = NULL; - DBG("remove %s", session->session_path); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) + return 0; - g_slist_free(session->user_allowed_bearers); - g_hash_table_destroy(session->service_hash); - g_sequence_free(session->service_list); + DBG(""); + + if (session->info->config.source_ip_rule) { + ipconfig = __connman_service_get_ip4config(session->service); + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && !ipconfig) + return 0; + } + + fw = __connman_firewall_create(); + if (!fw) + return -ENOMEM; - if (info->entry != NULL && - info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) { - __connman_service_disconnect(info->entry->service); + if (session->info->config.source_ip_rule && ipconfig) { + addr = __connman_ipconfig_get_local(ipconfig); } - free_session(session); + err =__connman_firewall_enable_marking(fw, + session->policy_config->id_type, + session->policy_config->id, + addr, session->mark); + if (err < 0) { + __connman_firewall_destroy(fw); + return err; + } + session->id_type = session->policy_config->id_type; + session->fw = fw; + + return 0; +} + +static void cleanup_firewall_session(struct connman_session *session) +{ + if (!session->fw) + return; + + __connman_firewall_disable_marking(session->fw); + __connman_firewall_disable_snat(session->fw); + __connman_firewall_destroy(session->fw); + + session->fw = NULL; } -static int assign_policy_plugin(struct connman_session *session) +static int init_routing_table(struct connman_session *session) { - if (session->policy != NULL) - return -EALREADY; + int err; - if (policy_list == NULL) + if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && + !session->info->config.source_ip_rule) return 0; - session->policy = policy_list->data; + if (!session->service) + return 0; - return 0; + DBG(""); + + err = __connman_inet_add_fwmark_rule(session->mark, + AF_INET, session->mark); + if (err < 0) + return err; + + err = __connman_inet_add_fwmark_rule(session->mark, + AF_INET6, session->mark); + if (err < 0) + __connman_inet_del_fwmark_rule(session->mark, + AF_INET, session->mark); + session->policy_routing = true; + + return err; } -static int create_policy_config(struct connman_session *session, - connman_session_config_cb callback, - void *user_data) +static void del_default_route(struct connman_session *session) { - struct connman_session_config *config; + if (!session->gateway) + return; - if (session->policy == NULL) { - config = connman_session_create_default_config(); - if (config == NULL) - return -ENOMEM; + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); - session->policy_config = config; + __connman_inet_del_subnet_from_table(session->mark, + session->index, session->gateway, session->prefixlen); - return 0; - } + __connman_inet_del_default_from_table(session->mark, + session->index, session->gateway); + g_free(session->gateway); + session->gateway = NULL; + session->prefixlen = 0; + session->index = -1; +} + +static void add_default_route(struct connman_session *session) +{ + struct connman_ipconfig *ipconfig; + int err; + struct in_addr addr = { INADDR_ANY }; + + if (!session->service) + return; - return (*session->policy->create)(session, callback, user_data); + ipconfig = __connman_service_get_ip4config(session->service); + session->index = __connman_ipconfig_get_index(ipconfig); + session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig)); + + if (!session->gateway) + session->gateway = g_strdup(inet_ntoa(addr)); + + session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig); + + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); + + err = __connman_inet_add_default_to_table(session->mark, + session->index, session->gateway); + if (err < 0) + DBG("session %p %s", session, strerror(-err)); + + err = __connman_inet_add_subnet_to_table(session->mark, + session->index, session->gateway, session->prefixlen); + if (err < 0) + DBG("session add subnet route %p %s", session, strerror(-err)); } -static void probe_policy(struct connman_session_policy *policy) +static void del_nat_rules(struct connman_session *session) { + struct fw_snat *fw_snat; - GHashTableIter iter; - gpointer key, value; - struct connman_session *session; + if (!session->snat_enabled) + return; - DBG("policy %p name %s", policy, policy->name); + session->snat_enabled = false; + fw_snat = fw_snat_lookup(session->index, session->addr); - g_hash_table_iter_init(&iter, session_hash); + if (!fw_snat) + return; - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; + fw_snat_unref(session, fw_snat); +} - if (session->policy != NULL) - continue; +static void add_nat_rules(struct connman_session *session) +{ + struct connman_ipconfig *ipconfig; + struct fw_snat *fw_snat; + const char *addr; + int index, err; + char *ifname; + + if (!session->service) + return; + + ipconfig = __connman_service_get_ip4config(session->service); + index = __connman_ipconfig_get_index(ipconfig); + ifname = connman_inet_ifname(index); + addr = __connman_ipconfig_get_local(ipconfig); + + if (!addr) + return; + + g_free(session->addr); + session->addr = g_strdup(addr); - assign_policy_plugin(session); + session->snat_enabled = true; + fw_snat = fw_snat_lookup(index, session->addr); + if (fw_snat) { + fw_snat_ref(session, fw_snat); + return; + } + + err = fw_snat_create(session, index, ifname, addr); + if (err < 0) { + DBG("failed to add SNAT rule"); + session->snat_enabled = false; } + + g_free(ifname); } -static void remove_policy(struct connman_session_policy *policy) +uint32_t connman_session_firewall_get_fwmark(struct connman_session *session) { - GHashTableIter iter; - gpointer key, value; - struct connman_session *session; + return session->mark; +} - DBG("policy %p name %s", policy, policy->name); +static void cleanup_routing_table(struct connman_session *session) +{ + DBG(""); - g_hash_table_iter_init(&iter, session_hash); + if (session->policy_routing) { + __connman_inet_del_fwmark_rule(session->mark, + AF_INET6, session->mark); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; + __connman_inet_del_fwmark_rule(session->mark, + AF_INET, session->mark); + session->policy_routing = false; + } - if (session->policy != policy) - continue; + del_default_route(session); +} + +static void update_firewall(struct connman_session *session) +{ + cleanup_firewall_session(session); + init_firewall_session(session); +} + +static void update_routing_table(struct connman_session *session) +{ + cleanup_routing_table(session); + init_routing_table(session); + add_default_route(session); +} + +static void cleanup_nat_rules(struct connman_session *session) +{ + del_nat_rules(session); +} - session->policy = NULL; - assign_policy_plugin(session); +static void destroy_policy_config(struct connman_session *session) +{ + if (!policy) { + g_free(session->policy_config); + return; } + + policy->destroy(session); } -static gint compare_priority(gconstpointer a, gconstpointer b) +static void free_session(struct connman_session *session) { - const struct connman_session_policy *policy1 = a; - const struct connman_session_policy *policy2 = b; + if (!session) + return; + + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); + + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + + destroy_policy_config(session); + g_slist_free(session->info->config.allowed_bearers); + g_free(session->info->config.allowed_interface); + g_free(session->owner); + g_free(session->session_path); + g_free(session->notify_path); + g_free(session->info); + g_free(session->info_last); + g_free(session->gateway); + g_free(session->addr); - return policy2->priority - policy1->priority; + g_free(session); } +static void set_active_session(struct connman_session *session, bool enable) +{ + + if (policy && policy->session_changed) + policy->session_changed(session, enable, + session->info->config.allowed_bearers); -int connman_session_policy_register(struct connman_session_policy *policy) + __connman_service_set_active_session(enable, + session->info->config.allowed_bearers); +} + +static void cleanup_session(gpointer user_data) { - DBG("name %s", policy->name); + struct connman_session *session = user_data; + + DBG("remove %s", session->session_path); + + cleanup_nat_rules(session); + cleanup_routing_table(session); + cleanup_firewall_session(session); - if (policy->create == NULL || policy->destroy == NULL) + if (session->active) + set_active_session(session, false); + + session_deactivate(session); + update_session_state(session); + + g_slist_free(session->user_allowed_bearers); + g_free(session->user_allowed_interface); + + free_session(session); +} + +struct creation_data { + DBusMessage *pending; + struct connman_session *session; + + /* user config */ + enum connman_session_type type; + GSList *allowed_bearers; + char *allowed_interface; + bool source_ip_rule; + char *context_identifier; +}; + +static void cleanup_creation_data(struct creation_data *creation_data) +{ + if (!creation_data) + return; + + if (creation_data->pending) + dbus_message_unref(creation_data->pending); + if (creation_data->context_identifier) + g_free(creation_data->context_identifier); + + g_slist_free(creation_data->allowed_bearers); + g_free(creation_data->allowed_interface); + g_free(creation_data); +} + +static int create_policy_config(struct connman_session *session, + connman_session_config_func_t cb, + struct creation_data *creation_data) +{ + struct connman_session_config *config; + + if (!policy) { + config = connman_session_create_default_config(); + if (!config) { + free_session(session); + cleanup_creation_data(creation_data); + return -ENOMEM; + } + + return cb(session, config, creation_data, 0); + } + + return policy->create(session, cb, creation_data); +} + +int connman_session_policy_register(struct connman_session_policy *plugin) +{ + if (policy) + return -EINVAL; + + if (!plugin->create || !plugin->destroy) return -EINVAL; - policy_list = g_slist_insert_sorted(policy_list, policy, - compare_priority); + DBG("name %s", plugin->name); - probe_policy(policy); + policy = plugin; return 0; } -void connman_session_policy_unregister(struct connman_session_policy *policy) +void connman_session_policy_unregister(struct connman_session_policy *plugin) { + if (plugin != policy) + return; + DBG("name %s", policy->name); - policy_list = g_slist_remove(policy_list, policy); + policy = NULL; +} + +static int default_bearers[] = { + CONNMAN_SERVICE_TYPE_ETHERNET, + CONNMAN_SERVICE_TYPE_WIFI, + CONNMAN_SERVICE_TYPE_BLUETOOTH, + CONNMAN_SERVICE_TYPE_CELLULAR, + CONNMAN_SERVICE_TYPE_GADGET, +}; + +static void add_default_bearer_types(GSList **list) +{ + unsigned int i; - remove_policy(policy); + for (i = 0; i < G_N_ELEMENTS(default_bearers); i++) + *list = g_slist_append(*list, + GINT_TO_POINTER(default_bearers[i])); } -int connman_session_set_default_config(struct connman_session_config *config) +void connman_session_set_default_config(struct connman_session_config *config) { + config->id_type = CONNMAN_SESSION_ID_TYPE_UNKNOWN; + g_free(config->id); + config->id = NULL; + config->priority = FALSE; config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT; config->type = CONNMAN_SESSION_TYPE_ANY; config->ecall = FALSE; g_slist_free(config->allowed_bearers); - config->allowed_bearers = g_slist_prepend(NULL, - GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN)); - if (config->allowed_bearers == NULL) - return -ENOMEM; - - return 0; + config->allowed_bearers = NULL; + add_default_bearer_types(&config->allowed_bearers); } struct connman_session_config *connman_session_create_default_config(void) { struct connman_session_config *config; - config = g_try_new0(struct connman_session_config, 1); - if (config == NULL) - return NULL; - - if (connman_session_set_default_config(config) < 0) { - g_free(config); - return NULL; - } + config = g_new0(struct connman_session_config, 1); + connman_session_set_default_config(config); return config; } @@ -447,6 +705,11 @@ int connman_session_parse_bearers(const char *token, GSList **list) if (g_strcmp0(token, "") == 0) return 0; + if (g_strcmp0(token, "*") == 0) { + add_default_bearer_types(list); + return 0; + } + err = bearer2service(token, &bearer); if (err < 0) return err; @@ -490,58 +753,51 @@ static int parse_bearers(DBusMessageIter *iter, GSList **list) return 0; } -static int filter_bearer(GSList *policy_bearers, - enum connman_service_type bearer, +static void filter_bearer(GSList *bearers, + enum connman_service_type policy, GSList **list) { - enum connman_service_type policy; + enum connman_service_type bearer; GSList *it; - if (policy_bearers == NULL) - goto clone; - - for (it = policy_bearers; it != NULL; it = it->next) { - policy = GPOINTER_TO_INT(it->data); + if (!bearers) + return; - if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) { - bearer = policy; - goto clone; - } + for (it = bearers; it; it = it->next) { + bearer = GPOINTER_TO_INT(it->data); - if (policy != CONNMAN_SERVICE_TYPE_UNKNOWN && policy != bearer) + if (policy != bearer) continue; - goto clone; + *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); + return; } +} + +static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, + GSList **list) +{ + enum connman_service_type policy_bearer; + GSList *it; *list = NULL; - return 0; - -clone: - *list = g_slist_append(*list, GINT_TO_POINTER(bearer)); + for (it = policy_bearers; it; it = it->next) { + policy_bearer = GPOINTER_TO_INT(it->data); - return 0; + filter_bearer(bearers, policy_bearer, list); + } } -static int apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers, - GSList **list) +static char * apply_policy_on_interface(const char *policy_interface, + const char *user_interface) { - enum connman_service_type bearer; - GSList *it; - int err; - - *list = NULL; - - for (it = bearers; it != NULL; it = it->next) { - bearer = GPOINTER_TO_INT(it->data); - - err = filter_bearer(policy_bearers, bearer, list); - if (err < 0) - return err; - } - - return 0; + if (policy_interface) + return g_strdup(policy_interface); + else if (user_interface) + return g_strdup(user_interface); + else + return NULL; } const char *connman_session_get_owner(struct connman_session *session) @@ -555,12 +811,12 @@ static void append_allowed_bearers(DBusMessageIter *iter, void *user_data) GSList *list; for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { + list; list = list->next) { enum connman_service_type bearer = GPOINTER_TO_INT(list->data); const char *name = __connman_service_type2string(bearer); - if (name == NULL) - name = "*"; + if (!name) + name = ""; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); @@ -572,16 +828,15 @@ static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) struct connman_service *service = user_data; struct connman_ipconfig *ipconfig_ipv4; - if (service == NULL) + if (!service) return; - if (__connman_service_is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) { + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4)) return; - } ipconfig_ipv4 = __connman_service_get_ip4config(service); - if (ipconfig_ipv4 == NULL) + if (!ipconfig_ipv4) return; __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter); @@ -592,17 +847,16 @@ static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) struct connman_service *service = user_data; struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6; - if (service == NULL) + if (!service) return; - if (__connman_service_is_connected_state(service, - CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) { + if (!__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV6)) return; - } ipconfig_ipv4 = __connman_service_get_ip4config(service); ipconfig_ipv6 = __connman_service_get_ip6config(service); - if (ipconfig_ipv6 == NULL) + if (!ipconfig_ipv6) return; __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4); @@ -614,10 +868,12 @@ static void append_notify(DBusMessageIter *dict, struct session_info *info = session->info; struct session_info *info_last = session->info_last; struct connman_service *service; - const char *name, *ifname, *bearer; + enum connman_service_type type; + const char *name, *bearer; + char *ifname; + int idx; - if (session->append_all == TRUE || - info->state != info_last->state) { + if (session->append_all || info->state != info_last->state) { const char *state = state2string(info->state); connman_dbus_dict_append_basic(dict, "State", @@ -626,18 +882,23 @@ static void append_notify(DBusMessageIter *dict, info_last->state = info->state; } - if (session->append_all == TRUE || - info->entry != info_last->entry) { - if (info->entry == NULL) { - name = ""; - ifname = ""; + if (session->append_all || session->service != session->service_last) { + if (session->service) { + service = session->service; + name = __connman_service_get_name(service); + idx = __connman_service_get_index(service); + + ifname = connman_inet_ifname(idx); + if (!ifname) + ifname = g_strdup(""); + + type = connman_service_get_type(service); + bearer = service2bearer(type); + } else { service = NULL; + name = ""; + ifname = g_strdup(""); bearer = ""; - } else { - name = info->entry->name; - ifname = info->entry->ifname; - service = info->entry->service; - bearer = info->entry->bearer; } connman_dbus_dict_append_basic(dict, "Name", @@ -660,10 +921,12 @@ static void append_notify(DBusMessageIter *dict, DBUS_TYPE_STRING, &bearer); - info_last->entry = info->entry; + g_free(ifname); + + session->service_last = session->service; } - if (session->append_all == TRUE || + if (session->append_all || info->config.type != info_last->config.type) { const char *type = type2string(info->config.type); @@ -673,7 +936,7 @@ static void append_notify(DBusMessageIter *dict, info_last->config.type = info->config.type; } - if (session->append_all == TRUE || + if (session->append_all || info->config.allowed_bearers != info_last->config.allowed_bearers) { connman_dbus_dict_append_array(dict, "AllowedBearers", DBUS_TYPE_STRING, @@ -682,53 +945,64 @@ static void append_notify(DBusMessageIter *dict, info_last->config.allowed_bearers = info->config.allowed_bearers; } - session->append_all = FALSE; -} + if (session->append_all || + info->config.allowed_interface != info_last->config.allowed_interface) { + char *ifname = info->config.allowed_interface; + if (!ifname) + ifname = "*"; + connman_dbus_dict_append_basic(dict, "AllowedInterface", + DBUS_TYPE_STRING, + &ifname); + info_last->config.allowed_interface = info->config.allowed_interface; + } -static connman_bool_t is_type_matching_state(enum connman_session_state *state, - enum connman_session_type type) -{ - switch (type) { - case CONNMAN_SESSION_TYPE_UNKNOWN: - return FALSE; - case CONNMAN_SESSION_TYPE_ANY: - return TRUE; - case CONNMAN_SESSION_TYPE_LOCAL: - if (*state >= CONNMAN_SESSION_STATE_CONNECTED) { - *state = CONNMAN_SESSION_STATE_CONNECTED; - return TRUE; - } + if (session->append_all || + info->config.context_identifier != info_last->config.context_identifier) { + char *ifname = info->config.context_identifier; + if (!ifname) + ifname = ""; + connman_dbus_dict_append_basic(dict, "ContextIdentifier", + DBUS_TYPE_STRING, + &ifname); + info_last->config.context_identifier = info->config.context_identifier; + } - break; - case CONNMAN_SESSION_TYPE_INTERNET: - if (*state == CONNMAN_SESSION_STATE_ONLINE) - return TRUE; - break; + if (session->append_all || + info->config.source_ip_rule != info_last->config.source_ip_rule) { + dbus_bool_t source_ip_rule = FALSE; + if (info->config.source_ip_rule) + source_ip_rule = TRUE; + connman_dbus_dict_append_basic(dict, "SourceIPRule", + DBUS_TYPE_BOOLEAN, + &source_ip_rule); + info_last->config.source_ip_rule = info->config.source_ip_rule; } - return FALSE; + session->append_all = false; } -static connman_bool_t compute_notifiable_changes(struct connman_session *session) +static bool compute_notifiable_changes(struct connman_session *session) { struct session_info *info_last = session->info_last; struct session_info *info = session->info; - if (session->append_all == TRUE) - return TRUE; + if (session->append_all) + return true; if (info->state != info_last->state) - return TRUE; + return true; - if (info->entry != info_last->entry && + if (session->service != session->service_last && info->state >= CONNMAN_SESSION_STATE_CONNECTED) - return TRUE; + return true; if (info->config.allowed_bearers != info_last->config.allowed_bearers || - info->config.type != info_last->config.type) - return TRUE; + info->config.type != info_last->config.type || + info->config.allowed_interface != info_last->config.allowed_interface || + info->config.source_ip_rule != info_last->config.source_ip_rule) + return true; - return FALSE; + return false; } static gboolean session_notify(gpointer user_data) @@ -737,660 +1011,49 @@ static gboolean session_notify(gpointer user_data) DBusMessage *msg; DBusMessageIter array, dict; - if (compute_notifiable_changes(session) == FALSE) - return FALSE; - - DBG("session %p owner %s notify_path %s", session, - session->owner, session->notify_path); - - msg = dbus_message_new_method_call(session->owner, session->notify_path, - CONNMAN_NOTIFICATION_INTERFACE, - "Update"); - if (msg == NULL) - return FALSE; - - dbus_message_iter_init_append(msg, &array); - connman_dbus_dict_open(&array, &dict); - - append_notify(&dict, session); - - connman_dbus_dict_close(&array, &dict); - - g_dbus_send_message(connection, msg); - - return FALSE; -} - -static void ipconfig_ipv4_changed(struct connman_session *session) -{ - struct session_info *info = session->info; - - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv4", append_ipconfig_ipv4, - info->entry->service); -} - -static void ipconfig_ipv6_changed(struct connman_session *session) -{ - struct session_info *info = session->info; - - connman_dbus_setting_changed_dict(session->owner, session->notify_path, - "IPv6", append_ipconfig_ipv6, - info->entry->service); -} - -static connman_bool_t service_type_match(struct connman_session *session, - struct connman_service *service) -{ - struct session_info *info = session->info; - GSList *list; - - for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { - enum connman_service_type bearer = GPOINTER_TO_INT(list->data); - enum connman_service_type service_type; - - if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) - return TRUE; - - service_type = connman_service_get_type(service); - if (bearer == service_type) - return TRUE; - } - - return FALSE; -} - -static connman_bool_t service_match(struct connman_session *session, - struct connman_service *service) -{ - if (service_type_match(session, service) == FALSE) - return FALSE; - - return TRUE; -} - -static int service_type_weight(enum connman_service_type type) -{ - /* - * The session doesn't care which service - * to use. Nevertheless we have to sort them - * according their type. The ordering is - * - * 1. Ethernet - * 2. Bluetooth - * 3. WiFi/WiMAX - * 4. Cellular - */ - - switch (type) { - case CONNMAN_SERVICE_TYPE_ETHERNET: - return 4; - case CONNMAN_SERVICE_TYPE_BLUETOOTH: - return 3; - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_WIMAX: - return 2; - case CONNMAN_SERVICE_TYPE_CELLULAR: - return 1; - case CONNMAN_SERVICE_TYPE_UNKNOWN: - case CONNMAN_SERVICE_TYPE_SYSTEM: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_GADGET: - break; - } - - return 0; -} - -static gint sort_allowed_bearers(struct connman_service *service_a, - struct connman_service *service_b, - struct connman_session *session) -{ - struct session_info *info = session->info; - GSList *list; - enum connman_service_type type_a, type_b; - int weight_a, weight_b; - - type_a = connman_service_get_type(service_a); - type_b = connman_service_get_type(service_b); - - for (list = info->config.allowed_bearers; - list != NULL; list = list->next) { - enum connman_service_type bearer = GPOINTER_TO_INT(list->data); - - if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) { - if (type_a != type_b) { - weight_a = service_type_weight(type_a); - weight_b = service_type_weight(type_b); - - if (weight_a > weight_b) - return -1; - - if (weight_a < weight_b) - return 1; - - return 0; - } - } - - if (type_a == bearer && type_b == bearer) - return 0; - - if (type_a == bearer && type_b != bearer) - return -1; - - if (type_a != bearer && type_b == bearer) - return 1; - } - - return 0; -} - -static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data) -{ - struct service_entry *entry_a = (void *)a; - struct service_entry *entry_b = (void *)b; - struct connman_session *session = user_data; - - return sort_allowed_bearers(entry_a->service, entry_b->service, - session); -} - -static enum connman_session_state service_to_session_state(enum connman_service_state state) -{ - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - case CONNMAN_SERVICE_STATE_READY: - return CONNMAN_SESSION_STATE_CONNECTED; - case CONNMAN_SERVICE_STATE_ONLINE: - return CONNMAN_SESSION_STATE_ONLINE; - } - - return CONNMAN_SESSION_STATE_DISCONNECTED; -} - -static connman_bool_t is_connected(enum connman_service_state state) -{ - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - return TRUE; - } - - return FALSE; -} - -static connman_bool_t is_connecting(enum connman_service_state state) -{ - switch (state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - break; - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - return TRUE; - case CONNMAN_SERVICE_STATE_DISCONNECT: - case CONNMAN_SERVICE_STATE_FAILURE: - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - break; - } - - return FALSE; -} - -static connman_bool_t explicit_connect(enum connman_session_reason reason) -{ - switch (reason) { - case CONNMAN_SESSION_REASON_UNKNOWN: - case CONNMAN_SESSION_REASON_FREE_RIDE: - break; - case CONNMAN_SESSION_REASON_CONNECT: - return TRUE; - } - - return FALSE; -} - -static connman_bool_t explicit_disconnect(struct session_info *info) -{ - if (info->entry == NULL) - return FALSE; - - DBG("reason %s service %p state %d", - reason2string(info->entry->reason), - info->entry->service, info->entry->state); - - if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN) - return FALSE; - - if (explicit_connect(info->entry->reason) == FALSE) - return FALSE; - - if (__connman_service_session_dec(info->entry->service) == FALSE) - return FALSE; - - return TRUE; -} - -struct pending_data { - unsigned int timeout; - struct service_entry *entry; - gboolean (*cb)(gpointer); -}; - -static void pending_timeout_free(gpointer data, gpointer user_data) -{ - struct pending_data *pending = data; - - DBG("pending %p timeout %d", pending, pending->timeout); - g_source_remove(pending->timeout); - g_free(pending); -} - -static void pending_timeout_remove_all(struct service_entry *entry) -{ - DBG(""); - - g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL); - g_slist_free(entry->pending_timeouts); - entry->pending_timeouts = NULL; -} - -static gboolean pending_timeout_cb(gpointer data) -{ - struct pending_data *pending = data; - struct service_entry *entry = pending->entry; - gboolean ret; - - DBG("pending %p timeout %d", pending, pending->timeout); - - ret = pending->cb(pending->entry); - if (ret == FALSE) { - entry->pending_timeouts = - g_slist_remove(entry->pending_timeouts, - pending); - g_free(pending); - } - return ret; -} - -static connman_bool_t pending_timeout_add(unsigned int seconds, - gboolean (*cb)(gpointer), - struct service_entry *entry) -{ - struct pending_data *pending = g_try_new0(struct pending_data, 1); - - if (pending == NULL || cb == NULL || entry == NULL) { - g_free(pending); - return FALSE; - } - - pending->cb = cb; - pending->entry = entry; - pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb, - pending); - entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts, - pending); - - DBG("pending %p entry %p timeout id %d", pending, entry, - pending->timeout); - - return TRUE; -} - -static gboolean call_disconnect(gpointer user_data) -{ - struct service_entry *entry = user_data; - struct connman_service *service = entry->service; - - /* - * TODO: We should mark this entry as pending work. In case - * disconnect fails we just unassign this session from the - * service and can't do anything later on it - */ - DBG("disconnect service %p", service); - __connman_service_disconnect(service); - - return FALSE; -} - -static gboolean call_connect(gpointer user_data) -{ - struct service_entry *entry = user_data; - struct connman_service *service = entry->service; - - DBG("connect service %p", service); - __connman_service_connect(service); - - return FALSE; -} - -static void deselect_service(struct session_info *info) -{ - struct service_entry *entry; - connman_bool_t disconnect, connected; - - DBG(""); - - if (info->entry == NULL) - return; - - disconnect = explicit_disconnect(info); - - connected = is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE; - - info->state = CONNMAN_SESSION_STATE_DISCONNECTED; - info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; - - entry = info->entry; - info->entry = NULL; - - DBG("disconnect %d connected %d", disconnect, connected); - - if (disconnect == TRUE && connected == TRUE) - pending_timeout_add(0, call_disconnect, entry); -} - -static void deselect_and_disconnect(struct connman_session *session) -{ - struct session_info *info = session->info; - - deselect_service(info); - - info->reason = CONNMAN_SESSION_REASON_FREE_RIDE; -} - -static void select_connected_service(struct session_info *info, - struct service_entry *entry) -{ - enum connman_session_state state; - - state = service_to_session_state(entry->state); - if (is_type_matching_state(&state, info->config.type) == FALSE) - return; - - info->state = state; - - info->entry = entry; - info->entry->reason = info->reason; - - if (explicit_connect(info->reason) == FALSE) - return; - - __connman_service_session_inc(info->entry->service); -} - -static void select_offline_service(struct session_info *info, - struct service_entry *entry) -{ - if (explicit_connect(info->reason) == FALSE) - return; - - info->state = service_to_session_state(entry->state); - - info->entry = entry; - info->entry->reason = info->reason; - - __connman_service_session_inc(info->entry->service); - pending_timeout_add(0, call_connect, entry); -} - -static void select_service(struct session_info *info, - struct service_entry *entry) -{ - DBG("service %p", entry->service); - - if (is_connected(entry->state) == TRUE) - select_connected_service(info, entry); - else - select_offline_service(info, entry); -} - -static void select_and_connect(struct connman_session *session, - enum connman_session_reason reason) -{ - struct session_info *info = session->info; - struct service_entry *entry = NULL; - GSequenceIter *iter; - - DBG("session %p reason %s", session, reason2string(reason)); - - info->reason = reason; - - iter = g_sequence_get_begin_iter(session->service_list); - - while (g_sequence_iter_is_end(iter) == FALSE) { - entry = g_sequence_get(iter); - - switch (entry->state) { - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_DISCONNECT: - select_service(info, entry); - return; - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_FAILURE: - break; - } - - iter = g_sequence_iter_next(iter); - } -} - -static struct service_entry *create_service_entry(struct connman_service *service, - const char *name, - enum connman_service_state state) -{ - struct service_entry *entry; - enum connman_service_type type; - int idx; - - entry = g_try_new0(struct service_entry, 1); - if (entry == NULL) - return entry; - - entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; - entry->state = state; - if (name != NULL) - entry->name = name; - else - entry->name = ""; - entry->service = service; - - idx = __connman_service_get_index(entry->service); - entry->ifname = connman_inet_ifname(idx); - if (entry->ifname == NULL) - entry->ifname = g_strdup(""); - - type = connman_service_get_type(entry->service); - entry->bearer = service2bearer(type); - - return entry; -} - -static void destroy_service_entry(gpointer data) -{ - struct service_entry *entry = data; - - pending_timeout_remove_all(entry); - g_free(entry->ifname); - - g_free(entry); -} - -static void populate_service_list(struct connman_session *session) -{ - struct service_entry *entry; - GSequenceIter *iter; - - session->service_hash = - g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, NULL); - session->service_list = __connman_service_get_list(session, - service_match, - create_service_entry, - destroy_service_entry); - - g_sequence_sort(session->service_list, sort_services, session); - - iter = g_sequence_get_begin_iter(session->service_list); - - while (g_sequence_iter_is_end(iter) == FALSE) { - entry = g_sequence_get(iter); - - DBG("service %p type %s name %s", entry->service, - service2bearer(connman_service_get_type(entry->service)), - entry->name); - - g_hash_table_replace(session->service_hash, - entry->service, iter); - - iter = g_sequence_iter_next(iter); - } -} - -static void session_changed(struct connman_session *session, - enum connman_session_trigger trigger) -{ - struct session_info *info = session->info; - struct session_info *info_last = session->info_last; - GSequenceIter *service_iter = NULL, *service_iter_last = NULL; - GSequence *service_list_last; - GHashTable *service_hash_last; - - /* - * TODO: This only a placeholder for the 'real' algorithm to - * play a bit around. So we are going to improve it step by step. - */ - - DBG("session %p trigger %s reason %s", session, trigger2string(trigger), - reason2string(info->reason)); - - if (info->entry != NULL) { - enum connman_session_state state; - - state = service_to_session_state(info->entry->state); - - if (is_type_matching_state(&state, info->config.type) == TRUE) - info->state = state; - } - - switch (trigger) { - case CONNMAN_SESSION_TRIGGER_UNKNOWN: - DBG("ignore session changed event"); - return; - case CONNMAN_SESSION_TRIGGER_SETTING: - if (info->config.allowed_bearers != info_last->config.allowed_bearers) { - - service_hash_last = session->service_hash; - service_list_last = session->service_list; - - populate_service_list(session); - - if (info->entry != NULL) { - service_iter_last = g_hash_table_lookup( - service_hash_last, - info->entry->service); - service_iter = g_hash_table_lookup( - session->service_hash, - info->entry->service); - } - - if (service_iter == NULL && service_iter_last != NULL) { - /* - * The currently selected service is - * not part of this session anymore. - */ - deselect_and_disconnect(session); - } - - g_hash_table_remove_all(service_hash_last); - g_sequence_free(service_list_last); - } - - if (info->config.type != info_last->config.type) { - if (info->state >= CONNMAN_SESSION_STATE_CONNECTED && - is_type_matching_state(&info->state, - info->config.type) == FALSE) - deselect_and_disconnect(session); - } - - if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) { - select_and_connect(session, - CONNMAN_SESSION_REASON_FREE_RIDE); - } + if (!compute_notifiable_changes(session)) + return FALSE; - break; - case CONNMAN_SESSION_TRIGGER_ECALL: - /* - * For the time beeing we fallback to normal connect - * strategy. - */ - case CONNMAN_SESSION_TRIGGER_CONNECT: - if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) { - if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) - break; - info->entry->reason = CONNMAN_SESSION_REASON_CONNECT; - __connman_service_session_inc(info->entry->service); - break; - } + DBG("session %p owner %s notify_path %s", session, + session->owner, session->notify_path); - if (info->entry != NULL && - is_connecting(info->entry->state) == TRUE) { - break; - } + msg = dbus_message_new_method_call(session->owner, session->notify_path, + CONNMAN_NOTIFICATION_INTERFACE, + "Update"); + if (!msg) + return FALSE; - select_and_connect(session, - CONNMAN_SESSION_REASON_CONNECT); + dbus_message_iter_init_append(msg, &array); + connman_dbus_dict_open(&array, &dict); - break; - case CONNMAN_SESSION_TRIGGER_DISCONNECT: - deselect_and_disconnect(session); + append_notify(&dict, session); - break; - case CONNMAN_SESSION_TRIGGER_SERVICE: - if (info->entry != NULL && - (is_connecting(info->entry->state) == TRUE || - is_connected(info->entry->state) == TRUE)) { - break; - } + connman_dbus_dict_close(&array, &dict); - deselect_and_disconnect(session); + g_dbus_send_message(connection, msg); - if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) { - select_and_connect(session, info->reason); - } + return FALSE; +} - break; - } +static void ipconfig_ipv4_changed(struct connman_session *session) +{ + connman_dbus_setting_changed_dict(session->owner, session->notify_path, + "IPv4", append_ipconfig_ipv4, + session->service); +} - session_notify(session); +static void ipconfig_ipv6_changed(struct connman_session *session) +{ + connman_dbus_setting_changed_dict(session->owner, session->notify_path, + "IPv6", append_ipconfig_ipv6, + session->service); } int connman_session_config_update(struct connman_session *session) { struct session_info *info = session->info; GSList *allowed_bearers; + char *allowed_interface; int err; DBG("session %p", session); @@ -1400,16 +1063,40 @@ int connman_session_config_update(struct connman_session *session) * might have changed. We can still optimize this later. */ - err = apply_policy_on_bearers( + if (session->id_type != session->policy_config->id_type) { + cleanup_firewall_session(session); + err = init_firewall_session(session); + if (err < 0) { + connman_session_destroy(session); + return err; + } + + session->id_type = session->policy_config->id_type; + } + + apply_policy_on_bearers( session->policy_config->allowed_bearers, session->user_allowed_bearers, &allowed_bearers); - if (err < 0) - return err; + + allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); g_slist_free(info->config.allowed_bearers); info->config.allowed_bearers = allowed_bearers; + g_free(info->config.allowed_interface); + info->config.allowed_interface = allowed_interface; + + session_activate(session); + info->config.type = apply_policy_on_type( session->policy_config->type, info->config.type); @@ -1417,12 +1104,12 @@ int connman_session_config_update(struct connman_session *session) info->config.roaming_policy = session->policy_config->roaming_policy; info->config.ecall = session->policy_config->ecall; - if (info->config.ecall == TRUE) + if (info->config.ecall) ecall_session = session; info->config.priority = session->policy_config->priority; - session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); + session_notify(session); return 0; } @@ -1434,14 +1121,21 @@ static DBusMessage *connect_session(DBusConnection *conn, DBG("session %p", session); - if (ecall_session != NULL) { - if (ecall_session->ecall == TRUE && ecall_session != session) + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) return __connman_error_failed(msg, EBUSY); - session->ecall = TRUE; - session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL); - } else - session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT); + session->ecall = true; + } + + if (!session->active) { + session->active = true; + set_active_session(session, true); + } + + session_activate(session); + + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_SESSION); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -1453,14 +1147,20 @@ static DBusMessage *disconnect_session(DBusConnection *conn, DBG("session %p", session); - if (ecall_session != NULL) { - if (ecall_session->ecall == TRUE && ecall_session != session) + if (ecall_session) { + if (ecall_session->ecall && ecall_session != session) return __connman_error_failed(msg, EBUSY); - session->ecall = FALSE; + session->ecall = false; + } + + if (session->active) { + session->active = false; + set_active_session(session, false); } - session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT); + session_deactivate(session); + update_session_state(session); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -1477,7 +1177,7 @@ static DBusMessage *change_session(DBusConnection *conn, int err; DBG("session %p", session); - if (dbus_message_iter_init(msg, &iter) == FALSE) + if (!dbus_message_iter_init(msg, &iter)) return __connman_error_invalid_arguments(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) @@ -1493,31 +1193,69 @@ static DBusMessage *change_session(DBusConnection *conn, switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_ARRAY: - if (g_str_equal(name, "AllowedBearers") == TRUE) { + if (g_str_equal(name, "AllowedBearers")) { err = parse_bearers(&value, &allowed_bearers); if (err < 0) - return __connman_error_failed(msg, err); + return __connman_error_failed(msg, -err); + + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); + update_session_state(session); g_slist_free(info->config.allowed_bearers); session->user_allowed_bearers = allowed_bearers; - err = apply_policy_on_bearers( + apply_policy_on_bearers( session->policy_config->allowed_bearers, session->user_allowed_bearers, &info->config.allowed_bearers); - if (err < 0) - return __connman_error_failed(msg, err); + session_activate(session); } else { goto err; } break; case DBUS_TYPE_STRING: - if (g_str_equal(name, "ConnectionType") == TRUE) { + if (g_str_equal(name, "ConnectionType")) { dbus_message_iter_get_basic(&value, &val); info->config.type = apply_policy_on_type( session->policy_config->type, connman_session_parse_connection_type(val)); + } else if (g_str_equal(name, "AllowedInterface")) { + dbus_message_iter_get_basic(&value, &val); + if (session->active) + set_active_session(session, false); + + session->active = false; + session_deactivate(session); + update_session_state(session); + + g_free(session->user_allowed_interface); + /* empty string means allow any interface */ + if (!g_strcmp0(val, "")) + session->user_allowed_interface = NULL; + else + session->user_allowed_interface = g_strdup(val); + + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); + + session_activate(session); + } else { + goto err; + } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(name, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + + info->config.source_ip_rule = source_ip_rule; + update_session_state(session); } else { goto err; } @@ -1526,7 +1264,7 @@ static DBusMessage *change_session(DBusConnection *conn, goto err; } - session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); + session_notify(session); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -1551,7 +1289,7 @@ static void release_session(gpointer key, gpointer value, gpointer user_data) session->notify_path, CONNMAN_NOTIFICATION_INTERFACE, "Release"); - if (message == NULL) + if (!message) return; dbus_message_set_no_reply(message, TRUE); @@ -1569,8 +1307,6 @@ static int session_disconnect(struct connman_session *session) g_dbus_unregister_interface(connection, session->session_path, CONNMAN_SESSION_INTERFACE); - deselect_and_disconnect(session); - g_hash_table_remove(session_hash, session->session_path); return 0; @@ -1592,7 +1328,7 @@ static DBusMessage *destroy_session(DBusConnection *conn, DBG("session %p", session); - if (ecall_session != NULL && ecall_session != session) + if (ecall_session && ecall_session != session) return __connman_error_failed(msg, EBUSY); session_disconnect(session); @@ -1611,91 +1347,105 @@ static const GDBusMethodTable session_methods[] = { { }, }; -struct user_config { - DBusMessage *pending; - - enum connman_session_type type; - GSList *allowed_bearers; -}; - -static int session_create_cb(struct connman_session *session, +static int session_policy_config_cb(struct connman_session *session, struct connman_session_config *config, void *user_data, int err) { - DBusMessage *reply; - struct user_config *user_config = user_data; + struct creation_data *creation_data = user_data; struct session_info *info, *info_last; + DBusMessage *reply; DBG("session %p config %p", session, config); - if (err != 0) - goto out; + if (err < 0) + goto err; session->policy_config = config; + session->info->config.source_ip_rule = creation_data->source_ip_rule; + + session->mark = session_mark++; + session->index = -1; + + err = init_firewall_session(session); + if (err < 0) + goto err; + + err = init_routing_table(session); + if (err < 0) + goto err; info = session->info; info_last = session->info_last; - if (session->policy_config->ecall == TRUE) + if (session->policy_config->ecall) ecall_session = session; info->state = CONNMAN_SESSION_STATE_DISCONNECTED; info->config.type = apply_policy_on_type( session->policy_config->type, - user_config->type); + creation_data->type); info->config.priority = session->policy_config->priority; info->config.roaming_policy = session->policy_config->roaming_policy; - info->entry = NULL; - session->user_allowed_bearers = user_config->allowed_bearers; + session->user_allowed_bearers = creation_data->allowed_bearers; + creation_data->allowed_bearers = NULL; - err = apply_policy_on_bearers( + session->user_allowed_interface = creation_data->allowed_interface; + creation_data->allowed_interface = NULL; + + apply_policy_on_bearers( session->policy_config->allowed_bearers, session->user_allowed_bearers, &info->config.allowed_bearers); - if (err < 0) - goto out; + + info->config.allowed_interface = apply_policy_on_interface( + session->policy_config->allowed_interface, + session->user_allowed_interface); g_hash_table_replace(session_hash, session->session_path, session); DBG("add %s", session->session_path); - if (g_dbus_register_interface(connection, session->session_path, + if (!g_dbus_register_interface(connection, session->session_path, CONNMAN_SESSION_INTERFACE, - session_methods, NULL, - NULL, session, NULL) == FALSE) { + session_methods, NULL, NULL, + session, NULL)) { connman_error("Failed to register %s", session->session_path); g_hash_table_remove(session_hash, session->session_path); err = -EINVAL; - goto out; + goto err_notify; } - reply = g_dbus_create_reply(user_config->pending, + reply = g_dbus_create_reply(creation_data->pending, DBUS_TYPE_OBJECT_PATH, &session->session_path, DBUS_TYPE_INVALID); g_dbus_send_message(connection, reply); - - populate_service_list(session); + creation_data->pending = NULL; info_last->state = info->state; info_last->config.priority = info->config.priority; info_last->config.roaming_policy = info->config.roaming_policy; - info_last->entry = info->entry; info_last->config.allowed_bearers = info->config.allowed_bearers; + info_last->config.allowed_interface = info->config.allowed_interface; + info_last->config.source_ip_rule = info->config.source_ip_rule; - session->append_all = TRUE; + session->append_all = true; - session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); + cleanup_creation_data(creation_data); -out: - if (err < 0) { - reply = __connman_error_failed(user_config->pending, -err); - g_dbus_send_message(connection, reply); + session_activate(session); - free_session(session); - } + return 0; + +err: + cleanup_session(session); + +err_notify: + reply = __connman_error_failed(creation_data->pending, -err); + g_dbus_send_message(connection, reply); + creation_data->pending = NULL; - g_free(user_config); + cleanup_creation_data(creation_data); return err; } @@ -1706,16 +1456,17 @@ int __connman_session_create(DBusMessage *msg) char *session_path = NULL; DBusMessageIter iter, array; struct connman_session *session = NULL; - struct user_config *user_config = NULL; - connman_bool_t user_allowed_bearers = FALSE; - connman_bool_t user_connection_type = FALSE; - int err; + struct creation_data *creation_data = NULL; + bool user_allowed_bearers = false; + bool user_connection_type = false; + int err, i; + char *str; owner = dbus_message_get_sender(msg); DBG("owner %s", owner); - if (ecall_session != NULL && ecall_session->ecall == TRUE) { + if (ecall_session && ecall_session->ecall) { /* * If there is an emergency call already going on, * ignore session creation attempt @@ -1724,13 +1475,13 @@ int __connman_session_create(DBusMessage *msg) goto err; } - user_config = g_try_new0(struct user_config, 1); - if (user_config == NULL) { + creation_data = g_try_new0(struct creation_data, 1); + if (!creation_data) { err = -ENOMEM; goto err; } - user_config->pending = dbus_message_ref(msg); + creation_data->pending = dbus_message_ref(msg); dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &array); @@ -1747,30 +1498,51 @@ int __connman_session_create(DBusMessage *msg) switch (dbus_message_iter_get_arg_type(&value)) { case DBUS_TYPE_ARRAY: - if (g_str_equal(key, "AllowedBearers") == TRUE) { + if (g_str_equal(key, "AllowedBearers")) { err = parse_bearers(&value, - &user_config->allowed_bearers); + &creation_data->allowed_bearers); if (err < 0) goto err; - user_allowed_bearers = TRUE; + user_allowed_bearers = true; } else { err = -EINVAL; goto err; } break; case DBUS_TYPE_STRING: - if (g_str_equal(key, "ConnectionType") == TRUE) { + if (g_str_equal(key, "ConnectionType")) { dbus_message_iter_get_basic(&value, &val); - user_config->type = + creation_data->type = connman_session_parse_connection_type(val); - user_connection_type = TRUE; + user_connection_type = true; + } else if (g_str_equal(key, "ContextIdentifier")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->context_identifier = g_strdup(val); + } else if (g_str_equal(key, "AllowedInterface")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->allowed_interface = g_strdup(val); + } else { + err = -EINVAL; + goto err; + } + break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(key, "SourceIPRule")) { + dbus_bool_t source_ip_rule; + dbus_message_iter_get_basic(&value, &source_ip_rule); + creation_data->source_ip_rule = source_ip_rule; } else { err = -EINVAL; goto err; } + break; + default: + err = -EINVAL; + goto err; } + dbus_message_iter_next(&array); } @@ -1780,36 +1552,40 @@ int __connman_session_create(DBusMessage *msg) * * For AllowedBearers this is '*', ... */ - if (user_allowed_bearers == FALSE) { - user_config->allowed_bearers = - g_slist_append(NULL, - GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN)); - if (user_config->allowed_bearers == NULL) { + if (!user_allowed_bearers) { + add_default_bearer_types(&creation_data->allowed_bearers); + if (!creation_data->allowed_bearers) { err = -ENOMEM; goto err; } } /* ... and for ConnectionType it is 'any'. */ - if (user_connection_type == FALSE) - user_config->type = CONNMAN_SESSION_TYPE_ANY; + if (!user_connection_type) + creation_data->type = CONNMAN_SESSION_TYPE_ANY; dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, ¬ify_path); - if (notify_path == NULL) { + if (!notify_path) { err = -EINVAL; goto err; } - session_path = g_strdup_printf("/sessions%s", notify_path); - if (session_path == NULL) { + str = g_strdup(owner); + for (i = 0; str[i] != '\0'; i++) + if (str[i] == ':' || str[i] == '.') + str[i] = '_'; + session_path = g_strdup_printf("/sessions/%s%s", str, notify_path); + g_free(str); + + if (!session_path) { err = -ENOMEM; goto err; } session = g_hash_table_lookup(session_hash, session_path); - if (session != NULL) { + if (session) { g_free(session_path); session = NULL; err = -EEXIST; @@ -1817,22 +1593,23 @@ int __connman_session_create(DBusMessage *msg) } session = g_try_new0(struct connman_session, 1); - if (session == NULL) { + if (!session) { g_free(session_path); err = -ENOMEM; goto err; } + creation_data->session = session; session->session_path = session_path; session->info = g_try_new0(struct session_info, 1); - if (session->info == NULL) { + if (!session->info) { err = -ENOMEM; goto err; } session->info_last = g_try_new0(struct session_info, 1); - if (session->info_last == NULL) { + if (!session->info_last) { err = -ENOMEM; goto err; } @@ -1843,13 +1620,10 @@ int __connman_session_create(DBusMessage *msg) g_dbus_add_disconnect_watch(connection, session->owner, owner_disconnect, session, NULL); - err = assign_policy_plugin(session); - if (err < 0) - goto err; - - err = create_policy_config(session, session_create_cb, user_config); - if (err < 0) - goto err; + err = create_policy_config(session, session_policy_config_cb, + creation_data); + if (err < 0 && err != -EINPROGRESS) + return err; return -EINPROGRESS; @@ -1858,14 +1632,18 @@ err: free_session(session); - if (user_config != NULL) { - dbus_message_unref(user_config->pending); - g_slist_free(user_config->allowed_bearers); - g_free(user_config); - } + cleanup_creation_data(creation_data); return err; } +bool __connman_session_policy_autoconnect(enum connman_service_connect_reason reason) +{ + if (!policy || !policy->autoconnect) + return true; + + return policy->autoconnect(reason); +} + void connman_session_destroy(struct connman_session *session) { DBG("session %p", session); @@ -1884,11 +1662,11 @@ int __connman_session_destroy(DBusMessage *msg) dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path, DBUS_TYPE_INVALID); - if (session_path == NULL) + if (!session_path) return -EINVAL; session = g_hash_table_lookup(session_hash, session_path); - if (session == NULL) + if (!session) return -EINVAL; if (g_strcmp0(owner, session->owner) != 0) @@ -1899,116 +1677,305 @@ int __connman_session_destroy(DBusMessage *msg) return 0; } -connman_bool_t __connman_session_mode() +int connman_session_connect(struct connman_service *service) +{ + DBG("service %p name %s", service, __connman_service_get_name(service)); + + return __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_SESSION); +} + +int connman_session_disconnect(struct connman_service *service) +{ + DBG("service %p", service); + + return __connman_service_disconnect(service); +} + +static enum connman_session_state service_to_session_state( + enum connman_service_state state) +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + return CONNMAN_SESSION_STATE_CONNECTED; + case CONNMAN_SERVICE_STATE_ONLINE: + return CONNMAN_SESSION_STATE_ONLINE; + } + + return CONNMAN_SESSION_STATE_DISCONNECTED; +} + +static void update_session_state(struct connman_session *session) { - return sessionmode; + enum connman_service_state service_state; + enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED; + + if (session->service) { + service_state = connman_service_get_state(session->service); + state = service_to_session_state(service_state); + session->info->state = state; + } + session->info->state = state; + + DBG("session %p state %s", session, state2string(state)); + + update_firewall(session); + del_nat_rules(session); + update_routing_table(session); + add_nat_rules(session); + + if (policy && policy->update_session_state) + policy->update_session_state(session, state); + + session_notify(session); } -void __connman_session_set_mode(connman_bool_t enable) +static bool session_match_service(struct connman_session *session, + struct connman_service *service) { - DBG("enable %d", enable); + enum connman_service_type bearer_type; + enum connman_service_type service_type; + enum connman_service_type current_service_type; + GSList *list; + char *ifname; + + if (policy && policy->allowed) + return policy->allowed(session, service); + + current_service_type = connman_service_get_type(session->service); + + for (list = session->info->config.allowed_bearers; list; list = list->next) { + bearer_type = GPOINTER_TO_INT(list->data); + service_type = connman_service_get_type(service); + ifname = connman_service_get_interface(service); + + +#if defined TIZEN_EXT + if (bearer_type == current_service_type) { + g_free(ifname); + return false; + } +#else + if (bearer_type == current_service_type) + return false; +#endif + + if (bearer_type == service_type && + (session->info->config.allowed_interface == NULL || + !g_strcmp0(session->info->config.allowed_interface, "*") || + !g_strcmp0(session->info->config.allowed_interface, ifname))) { + g_free(ifname); + return true; + } + g_free(ifname); + } + + return false; +} - if (sessionmode != enable) { - sessionmode = enable; +static bool is_session_connected(struct connman_session *session, + enum connman_service_state state) - connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "SessionMode", - DBUS_TYPE_BOOLEAN, &sessionmode); +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + if (session->info->config.type == CONNMAN_SESSION_TYPE_INTERNET) + return false; + /* fall through */ + case CONNMAN_SERVICE_STATE_ONLINE: + return true; } - if (sessionmode == TRUE) - __connman_service_disconnect_all(); + return false; } -static void service_add(struct connman_service *service, - const char *name) +static void session_activate(struct connman_session *session) { GHashTableIter iter; - GSequenceIter *iter_service_list; gpointer key, value; - struct connman_session *session; - struct service_entry *entry; - DBG("service %p", service); + if (!service_hash) + return; - g_hash_table_iter_init(&iter, session_hash); + if (policy && policy->get_service_for_session) { + struct connman_service *service; + struct connman_service_info *info; + GSList *service_list = NULL; + enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED; - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; + g_hash_table_iter_init(&iter, service_hash); - if (service_match(session, service) == FALSE) - continue; + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service_info *info = value; + state = connman_service_get_state(info->service); - entry = create_service_entry(service, name, - CONNMAN_SERVICE_STATE_IDLE); - if (entry == NULL) - continue; + if (is_session_connected(session, state)) + service_list = g_slist_prepend(service_list, + info->service); + } + + service_list = g_slist_reverse(service_list); + service = policy->get_service_for_session(session, service_list); + + if (service) { + info = g_hash_table_lookup(service_hash, service); + DBG("session %p add service %p", session, info->service); - iter_service_list = - g_sequence_insert_sorted(session->service_list, - entry, sort_services, + info->sessions = g_slist_prepend(info->sessions, session); + session->service = info->service; + update_session_state(session); + } + + g_slist_free(service_list); + return; + } - g_hash_table_replace(session->service_hash, service, - iter_service_list); + g_hash_table_iter_init(&iter, service_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_service_info *info = value; + enum connman_service_state state; - session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); + state = connman_service_get_state(info->service); + + if (is_session_connected(session, state) && + session_match_service(session, info->service)) { + DBG("session %p add service %p", session, info->service); + + info->sessions = g_slist_prepend(info->sessions, + session); + session->service = info->service; + update_session_state(session); + + return; + } } + + session_notify(session); } -static void service_remove(struct connman_service *service) +static void session_deactivate(struct connman_session *session) { + struct connman_service_info *info; + if (!service_hash) + return; + + if (!session->service) + return; + + info = g_hash_table_lookup(service_hash, session->service); + if (!info) + return; + + info->sessions = g_slist_remove(info->sessions, session); + session->service = NULL; + + session->info->state = CONNMAN_SESSION_STATE_DISCONNECTED; +} + +static void handle_service_state_online(struct connman_service *service, + enum connman_service_state state, + struct connman_service_info *info) +{ GHashTableIter iter; gpointer key, value; - struct connman_session *session; - struct session_info *info; - - DBG("service %p", service); g_hash_table_iter_init(&iter, session_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_session *session = value; + bool connected; - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - GSequenceIter *seq_iter; - session = value; - info = session->info; + connected = is_session_connected(session, state); + + if (session->service == service) { + if (!connected) { + DBG("session %p remove service %p", session, service); + info->sessions = g_slist_remove(info->sessions, + session); + session->service = NULL; + update_session_state(session); + } + } else if (connected && session_match_service(session, service)) { + DBG("session %p add service %p", session, service); + + info->sessions = g_slist_prepend(info->sessions, + session); + session->service = service; + update_session_state(session); + } + } +} + +static void handle_service_state_offline(struct connman_service *service, + struct connman_service_info *info) +{ + GSList *list; - seq_iter = g_hash_table_lookup(session->service_hash, service); - if (seq_iter == NULL) + for (list = info->sessions; list; list = list->next) { + struct connman_session *session = list->data; + + if (session->service != service) { + connman_warn("session %p should have session %p assigned", + session, service); continue; + } - g_sequence_remove(seq_iter); + DBG("session %p remove service %p", session, service); - if (info->entry != NULL && info->entry->service == service) - info->entry = NULL; - session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); + session->service = NULL; + update_session_state(session); + session_activate(session); } } static void service_state_changed(struct connman_service *service, - enum connman_service_state state) + enum connman_service_state state) { - GHashTableIter iter; - gpointer key, value; + struct connman_service_info *info; DBG("service %p state %d", service, state); - g_hash_table_iter_init(&iter, session_hash); + info = g_hash_table_lookup(service_hash, service); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - struct connman_session *session = value; - GSequenceIter *service_iter; + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + if (!info) + return; + + handle_service_state_offline(service, info); - service_iter = g_hash_table_lookup(session->service_hash, service); - if (service_iter != NULL) { - struct service_entry *entry; + g_hash_table_remove(service_hash, service); - entry = g_sequence_get(service_iter); - entry->state = state; + return; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + if (!info) { + info = g_new0(struct connman_service_info, 1); + g_hash_table_replace(service_hash, service, info); } - session_changed(session, - CONNMAN_SESSION_TRIGGER_SERVICE); + info->service = service; + handle_service_state_online(service, state, info); } } @@ -2027,14 +1994,16 @@ static void ipconfig_changed(struct connman_service *service, g_hash_table_iter_init(&iter, session_hash); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + while (g_hash_table_iter_next(&iter, &key, &value)) { session = value; info = session->info; if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) continue; - if (info->entry != NULL && info->entry->service == service) { + if (session->service && session->service == service) { + update_session_state(session); + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) ipconfig_ipv4_changed(session); else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) @@ -2043,10 +2012,8 @@ static void ipconfig_changed(struct connman_service *service, } } -static struct connman_notifier session_notifier = { +static const struct connman_notifier session_notifier = { .name = "session", - .service_add = service_add, - .service_remove = service_remove, .service_state_changed = service_state_changed, .ipconfig_changed = ipconfig_changed, }; @@ -2058,7 +2025,7 @@ int __connman_session_init(void) DBG(""); connection = connman_dbus_get_connection(); - if (connection == NULL) + if (!connection) return -1; err = connman_notifier_register(&session_notifier); @@ -2070,7 +2037,8 @@ int __connman_session_init(void) session_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, cleanup_session); - sessionmode = FALSE; + service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, cleanup_service); return 0; } @@ -2078,7 +2046,7 @@ void __connman_session_cleanup(void) { DBG(""); - if (connection == NULL) + if (!connection) return; connman_notifier_unregister(&session_notifier); @@ -2086,6 +2054,8 @@ void __connman_session_cleanup(void) g_hash_table_foreach(session_hash, release_session, NULL); g_hash_table_destroy(session_hash); session_hash = NULL; + g_hash_table_destroy(service_hash); + service_hash = NULL; dbus_connection_unref(connection); }