From: Patrik Flykt <[email protected]>

deselect_service() and select_offline_service() want to clear
up the session state machinery by setting up a zero second
timeout function thereby letting the main loop run once through
all waiting actions. Unfortunately, if a service is being
removed meantime, the service structure stored in the timeout
functions will become invalid.

This is fixed by adding a list of pending connect/disconnect
actions per struct service_entry and cancelling all of them
if the service is removed.

Fixes BMC#22879
---
 src/session.c |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/src/session.c b/src/session.c
index 384714f..4230e97 100644
--- a/src/session.c
+++ b/src/session.c
@@ -70,6 +70,7 @@ struct service_entry {
        struct connman_service *service;
        char *ifname;
        const char *bearer;
+       GSList *pending_timeouts;
 };
 
 struct session_info {
@@ -753,9 +754,75 @@ static connman_bool_t explicit_disconnect(struct 
session_info *info)
        return TRUE;
 }
 
+struct pending_data {
+       unsigned int timeout;
+       struct service_entry *entry;
+       gboolean (*cb)(gpointer);
+};
+
+static void pending_timeout_free(gpointer 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_free_full(entry->pending_timeouts, pending_timeout_free);
+       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 connman_service *service = 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
@@ -770,7 +837,8 @@ static gboolean call_disconnect(gpointer user_data)
 
 static gboolean call_connect(gpointer user_data)
 {
-       struct connman_service *service = user_data;
+       struct service_entry *entry = user_data;
+       struct connman_service *service = entry->service;
 
        DBG("connect service %p", service);
        __connman_service_connect(service);
@@ -780,7 +848,7 @@ static gboolean call_connect(gpointer user_data)
 
 static connman_bool_t deselect_service(struct session_info *info)
 {
-       struct connman_service *service;
+       struct service_entry *entry;
        connman_bool_t disconnect, online;
 
        DBG("");
@@ -796,13 +864,13 @@ static connman_bool_t deselect_service(struct 
session_info *info)
        info->online = FALSE;
        info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
 
-       service = info->entry->service;
+       entry = info->entry;
        info->entry = NULL;
 
        DBG("disconnect %d online %d", disconnect, online);
 
        if (disconnect == TRUE && online == TRUE)
-               g_timeout_add_seconds(0, call_disconnect, service);
+               pending_timeout_add(0, call_disconnect, entry);
 
        return TRUE;
 }
@@ -845,7 +913,7 @@ static connman_bool_t select_offline_service(struct 
session_info *info,
        info->entry->reason = info->reason;
 
        __connman_service_session_inc(info->entry->service);
-       g_timeout_add_seconds(0, call_connect, info->entry->service);
+       pending_timeout_add(0, call_connect, entry);
 
        return TRUE;
 }
@@ -931,6 +999,7 @@ static void destroy_service_entry(gpointer data)
 {
        struct service_entry *entry = data;
 
+       pending_timeout_remove_all(entry);
        g_free(entry->ifname);
 
        g_free(entry);
-- 
1.7.2.5

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

Reply via email to