From: Daniel Wagner <[email protected]>

Via notify_service_add() and notify_service_remove() we are
able to maintain a current list (hash table in this case)
of existing services. For each service we assign a list
of sessions which match the configuration critereas
(AllowedBearers).

Note the we do not use a GSList directly as value for the
hash table because updating the session list while iteratering
over the hash table is bad idea. Therefore we have the
small helper struct service_data.

When a service changes it state session.c is notify via
notify_service_state_change(). In this function we just
need to look at the list of sessions associated with
given service to figure out which session needs to
be updated. Either we assign the service to a session
via add_service_to_session() or we remove it via
remove_service_from_session().
---
 src/session.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 203 insertions(+), 7 deletions(-)

diff --git a/src/session.c b/src/session.c
index f57de7a..e027eec 100644
--- a/src/session.c
+++ b/src/session.c
@@ -34,6 +34,7 @@
 
 static DBusConnection *connection;
 static GHashTable *session_hash;
+static GHashTable *service_hash;
 static struct connman_session *ecall_session;
 static GSList *policy_list;
 static uint32_t session_mark = 256;
@@ -45,6 +46,10 @@ enum connman_session_state {
        CONNMAN_SESSION_STATE_ONLINE         = 2,
 };
 
+struct service_data {
+       GSList *sessions;
+};
+
 struct session_info {
        struct connman_session_config config;
        enum connman_session_state state;
@@ -179,6 +184,14 @@ static char *service2bearer(enum connman_service_type type)
        return "";
 }
 
+static void cleanup_service(gpointer data)
+{
+       struct service_data *service_data = data;
+
+       g_slist_free(service_data->sessions);
+       g_free(service_data);
+}
+
 static int init_firewall(void)
 {
        struct firewall_context *fw;
@@ -614,6 +627,107 @@ static gboolean session_notify(gpointer user_data)
        return FALSE;
 }
 
+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_service_state(struct connman_session *session)
+{
+       enum connman_service_state service_state;
+       enum connman_session_state state;
+
+       service_state = __connman_service_get_state(session->service);
+       state = service_to_session_state(service_state);
+
+       session->info->state = state;
+
+       update_routing_table(session);
+
+       session_notify(session);
+}
+
+static void add_service_to_session(struct connman_session *session,
+                       struct connman_service *service)
+{
+       DBG("session %p service %p", session, service);
+
+       session->service = service;
+       update_service_state(session);
+}
+
+static void remove_service_from_session(struct connman_session *session,
+                                       struct connman_service *service)
+{
+       DBG("session %p service %p", session, service);
+
+       session->info->connect = false;
+       session->info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
+
+       session->service = NULL;
+
+       update_routing_table(session);
+
+       session_notify(session);
+}
+
+static bool is_session_connected(struct connman_session *session,
+                               enum connman_service_state state)
+{
+       struct connman_session_config *config;
+
+       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:
+               config = &session->info->config;
+               if (config->type == CONNMAN_SESSION_TYPE_INTERNET)
+                       return false;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return true;
+       }
+
+       return false;
+}
+
+static bool match_session(struct connman_session *session,
+                               struct connman_service *service)
+{
+       struct connman_session_config *config;
+       GSList *list;
+
+       config = &session->info->config;
+
+       for (list = config->allowed_bearers; list; list = list->next) {
+               enum connman_service_type type = GPOINTER_TO_UINT(list->data);
+
+               if (type == connman_service_get_type(service))
+                       return true;
+       }
+
+       return false;
+}
+
 static void cleanup_session_final(struct connman_session *session)
 {
        struct session_info *info = session->info;
@@ -1592,13 +1706,87 @@ int __connman_session_destroy(DBusMessage *msg)
        return 0;
 }
 
-static void service_state_changed(struct connman_service *service,
+static void notify_service_add(struct connman_service *service,
+                               const char *name)
+{
+       struct service_data *service_data;
+       struct connman_session *session;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (g_hash_table_contains(service_hash, service))
+               return;
+
+       service_data = g_new0(struct service_data, 1);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               session = value;
+
+               if (!match_session(session, service))
+                       continue;
+
+               service_data->sessions =
+                       g_slist_prepend(service_data->sessions, session);
+       }
+
+       g_hash_table_replace(service_hash, service, service_data);
+}
+
+static void notify_service_remove(struct connman_service *service)
+{
+       struct connman_session *session;
+       struct service_data *service_data;
+       GSList *list;
+
+       service_data = g_hash_table_lookup(service_hash, service);
+       if (!service_data)
+               return;
+
+       for (list = service_data->sessions; list; list = list->next) {
+               session = list->data;
+
+               if (session->service != service)
+                       continue;
+
+               remove_service_from_session(session, service);
+       }
+
+       g_hash_table_remove(service_hash, service);
+}
+
+static void notify_service_state_changed(struct connman_service *service,
                                enum connman_service_state state)
 {
+       struct connman_session *session;
+       struct service_data *service_data;
+       bool connected;
+       GSList *list;
+
        DBG("service %p state %d", service, state);
+
+       service_data = g_hash_table_lookup(service_hash, service);
+       if (!service_data)
+               return;
+
+       for (list = service_data->sessions; list; list = list->next) {
+               session = list->data;
+
+               connected = is_session_connected(session, state);
+               if (session->service == service) {
+                       if (connected)
+                               update_service_state(session);
+                       else
+                               remove_service_from_session(session, service);
+               } else if (connected) {
+                       if (!session->service)
+                               add_service_to_session(session, service);
+               }
+       }
 }
 
-static void ipconfig_changed(struct connman_service *service,
+static void notify_ipconfig_changed(struct connman_service *service,
                                struct connman_ipconfig *ipconfig)
 {
        GHashTableIter iter;
@@ -1633,8 +1821,10 @@ static void ipconfig_changed(struct connman_service 
*service,
 
 static struct connman_notifier session_notifier = {
        .name                   = "session",
-       .service_state_changed  = service_state_changed,
-       .ipconfig_changed       = ipconfig_changed,
+       .service_add            = notify_service_add,
+       .service_remove         = notify_service_remove,
+       .service_state_changed  = notify_service_state_changed,
+       .ipconfig_changed       = notify_ipconfig_changed,
 };
 
 static int session_nfacct_flush_cb(unsigned int error, void *user_data)
@@ -1657,15 +1847,18 @@ int __connman_session_init(void)
        if (!connection)
                return -1;
 
+       session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, cleanup_session);
+
+       service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                               NULL, cleanup_service);
+
        err = connman_notifier_register(&session_notifier);
        if (err < 0) {
                dbus_connection_unref(connection);
                return err;
        }
 
-       session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               NULL, cleanup_session);
-
        if (__connman_firewall_is_up()) {
                err = init_firewall();
                if (err < 0)
@@ -1691,5 +1884,8 @@ void __connman_session_cleanup(void)
        g_hash_table_destroy(session_hash);
        session_hash = NULL;
 
+       g_hash_table_destroy(service_hash);
+       service_hash = NULL;
+
        dbus_connection_unref(connection);
 }
-- 
1.8.4.474.g128a96c

_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman

Reply via email to