Module: sip-router Branch: admorten/sca Commit: 49130d3aca2dcb9fc959455ee580ed02234e4171 URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=49130d3a...
Author: Andrew Mortensen admorten@isc.upenn.edu Committer: Andrew Mortensen admorten@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 ));