Module: sip-router
Branch: admorten/sca
Commit: 49130d3aca2dcb9fc959455ee580ed02234e4171
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=49130d3…
Author: Andrew Mortensen <admorten(a)isc.upenn.edu>
Committer: Andrew Mortensen <admorten(a)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( ¬ify_list->aor ));
+
+ if ( sca_notify_call_info_subscribers( scam,
+ ¬ify_list->aor ) < 0 ) {
+ LM_ERR( "sca_appearance_purge_stale: failed to send "
+ "call-info NOTIFY %.*s subscribers",
+ STR_FMT( ¬ify_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,
+ ¶ms->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 ));