Fix worker thread resource leak in SIP TCP/TLS.
[asterisk/asterisk.git] / channels / chan_sip.c
index cb43652..417e7e3 100644 (file)
@@ -1510,7 +1510,7 @@ static int get_domain(const char *str, char *domain, int len);
 static void get_realm(struct sip_pvt *p, const struct sip_request *req);
 
 /*-- TCP connection handling ---*/
-static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session);
+static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session);
 static void *sip_tcp_worker_fn(void *);
 
 /*--- Constructing requests and responses */
@@ -2488,7 +2488,7 @@ static void *sip_tcp_worker_fn(void *data)
 {
        struct ast_tcptls_session_instance *tcptls_session = data;
 
-       return _sip_tcp_helper_thread(NULL, tcptls_session);
+       return _sip_tcp_helper_thread(tcptls_session);
 }
 
 /*! \brief Check if the authtimeout has expired.
@@ -2519,7 +2519,7 @@ static int sip_check_authtimeout(time_t start)
 /*! \brief SIP TCP thread management function
        This function reads from the socket, parses the packet into a request
 */
-static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
+static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session)
 {
        int res, cl, timeout = -1, authenticated = 0, flags, after_poll = 0, need_poll = 1;
        time_t start;
@@ -26483,6 +26483,7 @@ static int sip_prepare_socket(struct sip_pvt *p)
        struct ast_tcptls_session_instance *tcptls_session;
        struct ast_tcptls_session_args *ca;
        struct ast_sockaddr sa_tmp;
+       pthread_t launched;
 
        /* check to see if a socket is already active */
        if ((s->fd != -1) && (s->type == SIP_TRANSPORT_UDP)) {
@@ -26507,7 +26508,7 @@ static int sip_prepare_socket(struct sip_pvt *p)
        }
 
        /* At this point we are dealing with a TCP/TLS connection
-        * 1. We need to check to see if a connectin thread exists
+        * 1. We need to check to see if a connection thread exists
         *    for this address, if so use that.
         * 2. If a thread does not exist for this address, but the tcptls_session
         *    exists on the socket, the connection was closed.
@@ -26578,7 +26579,7 @@ static int sip_prepare_socket(struct sip_pvt *p)
        /* Give the new thread a reference to the tcptls_session */
        ao2_ref(s->tcptls_session, +1);
 
-       if (ast_pthread_create_background(&ca->master, NULL, sip_tcp_worker_fn, s->tcptls_session)) {
+       if (ast_pthread_create_detached_background(&launched, NULL, sip_tcp_worker_fn, s->tcptls_session)) {
                ast_debug(1, "Unable to launch '%s'.", ca->name);
                ao2_ref(s->tcptls_session, -1); /* take away the thread ref we just gave it */
                goto create_tcptls_session_fail;
@@ -31578,6 +31579,7 @@ static int unload_module(void)
        struct sip_threadinfo *th;
        struct ast_context *con;
        struct ao2_iterator i;
+       int wait_count;
 
        network_change_event_unsubscribe();
 
@@ -31636,7 +31638,6 @@ static int unload_module(void)
                pthread_t thread = th->threadid;
                th->stop = 1;
                pthread_kill(thread, SIGURG);
-               pthread_join(thread, NULL);
                ao2_t_ref(th, -1, "decrement ref from iterator");
        }
        ao2_iterator_destroy(&i);
@@ -31682,21 +31683,11 @@ static int unload_module(void)
        sip_epa_unregister_all();
        destroy_escs();
 
-       if (default_tls_cfg.certfile) {
-               ast_free(default_tls_cfg.certfile);
-       }
-       if (default_tls_cfg.pvtfile) {
-               ast_free(default_tls_cfg.pvtfile);
-       }
-       if (default_tls_cfg.cipher) {
-               ast_free(default_tls_cfg.cipher);
-       }
-       if (default_tls_cfg.cafile) {
-               ast_free(default_tls_cfg.cafile);
-       }
-       if (default_tls_cfg.capath) {
-               ast_free(default_tls_cfg.capath);
-       }
+       ast_free(default_tls_cfg.certfile);
+       ast_free(default_tls_cfg.pvtfile);
+       ast_free(default_tls_cfg.cipher);
+       ast_free(default_tls_cfg.cafile);
+       ast_free(default_tls_cfg.capath);
 
        cleanup_all_regs();
        ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
@@ -31714,6 +31705,21 @@ static int unload_module(void)
        ASTOBJ_CONTAINER_DESTROYALL(&submwil, sip_subscribe_mwi_destroy);
        ASTOBJ_CONTAINER_DESTROY(&submwil);
 
+       /*
+        * Wait awhile for the TCP/TLS thread container to become empty.
+        *
+        * XXX This is a hack, but the worker threads cannot be created
+        * joinable.  They can die on their own and remove themselves
+        * from the container thus resulting in a huge memory leak.
+        */
+       wait_count = 1000;
+       while (ao2_container_count(threadt) && --wait_count) {
+               sched_yield();
+       }
+       if (!wait_count) {
+               ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
+       }
+
        ao2_t_ref(peers, -1, "unref the peers table");
        ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
        ao2_t_ref(dialogs, -1, "unref the dialogs table");