[sr-dev] git:admorten/sca: modules/sca: detect and clear orphaned appearances caused by answer glare

Andrew Mortensen admorten at isc.upenn.edu
Thu Jun 13 21:33:05 CEST 2013


Module: sip-router
Branch: admorten/sca
Commit: 49130d3aca2dcb9fc959455ee580ed02234e4171
URL:    http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=49130d3aca2dcb9fc959455ee580ed02234e4171

Author: Andrew Mortensen <admorten at isc.upenn.edu>
Committer: Andrew Mortensen <admorten at isc.upenn.edu>
Date:   Thu Jun 13 15:30:40 2013 -0400

modules/sca: detect and clear orphaned appearances caused by answer glare

- set appearance state created by SCA callee answer to ACTIVE_PENDING,
  and promote to ACTIVE on ACK from caller. If no ACK from caller is
  received within 30 seconds (enough time for retransmission to fail),
  the ACTIVE_PENDING appearance will be cleared by the
  sca_appearance_purge_stale timer.

---

 modules/sca/sca.c            |    4 +-
 modules/sca/sca_appearance.c |  120 ++++++++++++++++++++++++++++++++++++++++++
 modules/sca/sca_appearance.h |   10 ++++
 modules/sca/sca_call_info.c  |   20 ++++++-
 4 files changed, 150 insertions(+), 4 deletions(-)

diff --git a/modules/sca/sca.c b/modules/sca/sca.c
index 9d407ad..d0db43b 100644
--- a/modules/sca/sca.c
+++ b/modules/sca/sca.c
@@ -30,6 +30,7 @@
 #include "../../timer_proc.h"
 
 #include "sca.h"
+#include "sca_appearance.h"
 #include "sca_db.h"
 #include "sca_call_info.h"
 #include "sca_rpc.h"
@@ -326,8 +327,7 @@ sca_mod_init( void )
 
     sca_subscriptions_restore_from_db( sca );
 
-    /* start timer to clear expired subscriptions */
-    register_timer( sca_subscription_purge_expired, sca,
+    register_timer( sca_appearance_purge_stale, sca,
 		    sca->cfg->purge_expired_interval );
 
     /*
diff --git a/modules/sca/sca_appearance.c b/modules/sca/sca_appearance.c
index f9c750c..7904abb 100644
--- a/modules/sca/sca_appearance.c
+++ b/modules/sca/sca_appearance.c
@@ -28,6 +28,7 @@
 #include "sca.h"
 #include "sca_appearance.h"
 #include "sca_hash.h"
+#include "sca_notify.h"
 #include "sca_util.h"
 
 const str SCA_APPEARANCE_INDEX_STR = STR_STATIC_INIT( "appearance-index" );
@@ -44,12 +45,14 @@ const str SCA_APPEARANCE_STATE_STR_HELD_PRIVATE = STR_STATIC_INIT("held-private"
 const str SCA_APPEARANCE_STATE_STR_UNKNOWN = STR_STATIC_INIT( "unknown" );
 
 
+/* STR_ACTIVE is repeated, once for ACTIVE_PENDING, once for ACTIVE */
 const str	*state_names[] = {
 			&SCA_APPEARANCE_STATE_STR_IDLE,
 			&SCA_APPEARANCE_STATE_STR_SEIZED,
 			&SCA_APPEARANCE_STATE_STR_PROGRESSING,
 			&SCA_APPEARANCE_STATE_STR_ALERTING,
 			&SCA_APPEARANCE_STATE_STR_ACTIVE,
+			&SCA_APPEARANCE_STATE_STR_ACTIVE,
 			&SCA_APPEARANCE_STATE_STR_HELD,
 			&SCA_APPEARANCE_STATE_STR_HELD_PRIVATE,
 		};
@@ -1209,3 +1212,120 @@ done:
 
     return( app );
 }
+
+    void
+sca_appearance_purge_stale( unsigned int ticks, void *param )
+{
+    struct notify_list {
+	struct notify_list	*next;
+	str			aor;
+    };
+
+    sca_mod		*scam = (sca_mod *)param;
+    sca_hash_table	*ht;
+    sca_hash_entry	*ent;
+    sca_appearance_list	*app_list;
+    sca_appearance	**cur_app, **tmp_app, *app = NULL;
+    struct notify_list	*notify_list = NULL, *tmp_nl;
+    int			i;
+    int			unlinked;
+    time_t		now;
+
+    LM_INFO( "SCA: purging stale appearances" );
+
+    assert( scam != NULL );
+    assert( scam->appearances != NULL );
+
+    now = time( NULL );
+
+    ht = scam->appearances;
+    for ( i = 0; i < ht->size; i++ ) {
+	sca_hash_table_lock_index( ht, i );
+
+	for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) {
+	    app_list = (sca_appearance_list *)ent->value;
+	    if ( app_list == NULL ) {
+		continue;
+	    }
+
+	    unlinked = 0;
+
+	    for ( cur_app = &app_list->appearances; *cur_app != NULL;
+			cur_app = tmp_app ) {
+		tmp_app = &(*cur_app)->next;
+
+		if ((*cur_app)->state != SCA_APPEARANCE_STATE_ACTIVE_PENDING ) {
+		    continue;
+		}
+		if (( now - (*cur_app)->times.mtime ) <
+			SCA_APPEARANCE_STATE_PENDING_TTL ) {
+		    continue;
+		}
+
+		/* unlink stale appearance */
+		app = *cur_app;
+		*cur_app = (*cur_app)->next;
+		tmp_app = cur_app;
+
+		if ( app ) {
+		    sca_appearance_free( app );
+		}
+
+		if ( unlinked ) {
+		    /* we've already added this AoR to the NOTIFY list */
+		    continue;
+		}
+		unlinked++;
+
+		/*
+		 * can't notify while slot is locked. make a list of AoRs to
+		 * notify after unlocking.
+		 */
+		tmp_nl = (struct notify_list *)pkg_malloc(
+				sizeof( struct notify_list ));
+		if ( tmp_nl == NULL ) {
+		    LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc "
+			    "notify list entry for %.*s",
+			    STR_FMT( &app_list->aor ));
+		    continue;
+		}
+
+		tmp_nl->aor.s = (char *)pkg_malloc( app_list->aor.len );
+		if ( tmp_nl->aor.s == NULL ) {
+		    LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc "
+			    "space for copy of %.*s",
+			    STR_FMT( &app_list->aor ));
+		    pkg_free( tmp_nl );
+		    continue;
+		}
+		SCA_STR_COPY( &tmp_nl->aor, &app_list->aor );
+
+		/* simple insert-at-head. order doesn't matter. */
+		tmp_nl->next = notify_list;
+		notify_list = tmp_nl;
+	    }
+	}
+
+	sca_hash_table_unlock_index( ht, i );
+
+	for ( ; notify_list != NULL; notify_list = tmp_nl ) {
+	    tmp_nl = notify_list->next;
+
+	    LM_INFO( "sca_appearance_purge_stale: notifying %.*s call-info "
+		    "subscribers", STR_FMT( &notify_list->aor ));
+
+	    if ( sca_notify_call_info_subscribers( scam,
+			&notify_list->aor ) < 0 ) {
+		LM_ERR( "sca_appearance_purge_stale: failed to send "
+			"call-info NOTIFY %.*s subscribers",
+			STR_FMT( &notify_list->aor ));
+		/* fall through, free memory anyway */
+	    }
+
+	    if ( notify_list->aor.s ) {
+		pkg_free( notify_list->aor.s );
+	    }
+	    pkg_free( notify_list );
+	}
+    }
+}
diff --git a/modules/sca/sca_appearance.h b/modules/sca/sca_appearance.h
index 5f8e1e6..a1feab7 100644
--- a/modules/sca/sca_appearance.h
+++ b/modules/sca/sca_appearance.h
@@ -33,6 +33,7 @@ enum {
     SCA_APPEARANCE_STATE_SEIZED,
     SCA_APPEARANCE_STATE_PROGRESSING,
     SCA_APPEARANCE_STATE_ALERTING,
+    SCA_APPEARANCE_STATE_ACTIVE_PENDING,
     SCA_APPEARANCE_STATE_ACTIVE,
     SCA_APPEARANCE_STATE_HELD,
     SCA_APPEARANCE_STATE_HELD_PRIVATE,
@@ -58,6 +59,13 @@ enum {
 };
 #define SCA_APPEARANCE_INDEX_UNAVAILABLE	-2
 
+/*
+ * maximum lifetime of an active, pending appearance.
+ * enough to allow retransmissions of the caller's
+ * ACK. on receipt of the caller's ACK, we promote
+ * the SCA callee's state to active.
+ */
+#define SCA_APPEARANCE_STATE_PENDING_TTL	30
 
 extern const str SCA_APPEARANCE_INDEX_STR;
 extern const str SCA_APPEARANCE_STATE_STR;
@@ -154,4 +162,6 @@ void		sca_appearance_free( sca_appearance * );
 int		sca_uri_is_shared_appearance( sca_mod *, str * );
 int		sca_uri_lock_shared_appearance( sca_mod *, str * );
 int		sca_uri_lock_if_shared_appearance( sca_mod *, str *, int * );
+
+void		sca_appearance_purge_stale( unsigned int, void * );
 #endif /* SCA_APPEARANCE_H */
diff --git a/modules/sca/sca_call_info.c b/modules/sca/sca_call_info.c
index 344ea27..487baa4 100644
--- a/modules/sca/sca_call_info.c
+++ b/modules/sca/sca_call_info.c
@@ -812,7 +812,8 @@ sca_call_info_uri_update( str *aor, sca_call_info *call_info,
 		STR_FMT( &to->uri ), STR_FMT( call_id ),
 		STR_FMT( &app->dialog.id ));
 
-	if ( sca_appearance_update_unsafe( app, SCA_APPEARANCE_STATE_ACTIVE,
+	if ( sca_appearance_update_unsafe( app,
+		SCA_APPEARANCE_STATE_ACTIVE_PENDING,
 		&from->display, &from->uri, &dialog, contact_uri,
 		&from->uri ) < 0 ) {
 	    sca_appearance_state_to_str( call_info->state, &state_str );
@@ -1442,8 +1443,10 @@ done:
 sca_call_info_ack_cb( struct cell *t, int type, struct tmcb_params *params )
 {
     struct to_body	*to;
+    sca_appearance	*app = NULL;
     str			from_aor = STR_NULL;
     str			to_aor = STR_NULL;
+    int			slot_idx = -1;
 
     if ( !(type & TMCB_E2EACK_IN)) {
 	return;
@@ -1465,12 +1468,25 @@ sca_call_info_ack_cb( struct cell *t, int type, struct tmcb_params *params )
 
     sca_call_info_ack_from_handler( params->req, &from_aor, &to_aor );
 
-    if ( !sca_uri_is_shared_appearance( sca, &to_aor )) {
+    if ( !sca_uri_lock_if_shared_appearance( sca, &to_aor, &slot_idx )) {
 	LM_DBG( "sca_call_info_ack_cb: %.*s is not a shared appearance",
 		STR_FMT( &to_aor ));
 	goto done;
     }
 
+    /* on ACK, ensure SCA callee state is promoted to ACTIVE. */
+    app = sca_appearance_for_tags_unsafe( sca, &to_aor,
+		&params->req->callid->body, &to->tag_value, NULL, slot_idx );
+    if ( app && app->state == SCA_APPEARANCE_STATE_ACTIVE_PENDING ) {
+	LM_DBG( "promoting %.*s appearance-index %d to active",
+		STR_FMT( &to_aor ), app->index );
+	sca_appearance_update_state_unsafe( app, SCA_APPEARANCE_STATE_ACTIVE );
+    }
+
+    if ( slot_idx >= 0 ) {
+	sca_hash_table_unlock_index( sca->appearances, slot_idx );
+    }
+
     if ( sca_notify_call_info_subscribers( sca, &to_aor ) < 0 ) {
 	LM_ERR( "sca_call_info_ack_cb: failed to call-info "
 		"NOTIFY %.*s subscribers", STR_FMT( &to_aor ));




More information about the sr-dev mailing list