@ -1,7 +1,7 @@
/*
* Asterisk - - An open source telephony toolkit .
*
* Copyright ( C ) 1999 - 20 06 , Digium , Inc .
* Copyright ( C ) 1999 - 20 12 , Digium , Inc .
*
* Mark Spencer < markster @ digium . com >
*
@ -374,6 +374,43 @@ static const struct ast_channel_tech agent_tech = {
. set_base_channel = agent_set_base_channel ,
} ;
/*!
* \ brief Locks the owning channel for a LOCKED pvt while obeying locking order . The pvt
* must enter this function locked and will be returned locked , but this function will
* unlock the pvt for a short time , so it can ' t be used while expecting the pvt to remain
* static . If function returns a non NULL channel , it will need to be unlocked and
* unrefed once it is no longer needed .
*
* \ param pvt Pointer to the LOCKED agent_pvt for which the owner is needed
* \ ret locked channel which owns the pvt at the time of completion . NULL if not available .
*/
static struct ast_channel * agent_lock_owner ( struct agent_pvt * pvt )
{
struct ast_channel * owner ;
for ( ; ; ) {
if ( ! pvt - > owner ) { /* No owner. Nothing to do. */
return NULL ;
}
/* If we don't ref the owner, it could be killed when we unlock the pvt. */
owner = ast_channel_ref ( pvt - > owner ) ;
/* Locking order requires us to lock channel, then pvt. */
ast_mutex_unlock ( & pvt - > lock ) ;
ast_channel_lock ( owner ) ;
ast_mutex_lock ( & pvt - > lock ) ;
/* Check if owner changed during pvt unlock period */
if ( owner ! = pvt - > owner ) { /* Channel changed. Unref and do another pass. */
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
} else { /* Channel stayed the same. Return it. */
return owner ;
}
}
}
/*!
* Adds an agent to the global list of agents .
*
@ -554,7 +591,11 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
struct ast_frame * f = NULL ;
static struct ast_frame answer_frame = { AST_FRAME_CONTROL , { AST_CONTROL_ANSWER } } ;
int cur_time = time ( NULL ) ;
struct ast_channel * owner ;
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
CHECK_FORMATS ( ast , p ) ;
if ( ! p - > start ) {
p - > start = cur_time ;
@ -584,13 +625,11 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
int howlong = cur_time - p - > start ;
if ( p - > autologoff & & ( howlong > = p - > autologoff ) ) {
ast_log ( LOG_NOTICE , " Agent '%s' didn't answer/confirm within %d seconds (waited %d) \n " , p - > name , p - > autologoff , howlong ) ;
if ( p - > owner | | p - > chan ) {
while ( p - > owner & & ast_channel_trylock ( p - > owner ) ) {
DEADLOCK_AVOIDANCE ( & p - > lock ) ;
}
if ( p - > owner ) {
ast_softhangup ( p - > owner , AST_SOFTHANGUP_EXPLICIT ) ;
ast_channel_unlock ( p - > owner ) ;
if ( owner | | p - > chan ) {
if ( owner ) {
ast_softhangup ( owner , AST_SOFTHANGUP_EXPLICIT ) ;
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
while ( p - > chan & & ast_channel_trylock ( p - > chan ) ) {
@ -652,6 +691,11 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
}
}
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
CLEANUP ( ast , p ) ;
if ( p - > chan & & ! p - > chan - > _bridge ) {
if ( strcasecmp ( p - > chan - > tech - > type , " Local " ) ) {
@ -887,6 +931,14 @@ int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
static int agent_hangup ( struct ast_channel * ast )
{
struct agent_pvt * p = ast - > tech_pvt ;
struct ast_channel * indicate_chan = NULL ;
char * tmp_moh ; /* moh buffer for indicating after unlocking p */
if ( p - > pending ) {
AST_LIST_LOCK ( & agents ) ;
AST_LIST_REMOVE ( & agents , p , list ) ;
AST_LIST_UNLOCK ( & agents ) ;
}
ast_mutex_lock ( & p - > lock ) ;
p - > owner = NULL ;
@ -909,7 +961,7 @@ static int agent_hangup(struct ast_channel *ast)
if ( p - > start & & ( ast - > _state ! = AST_STATE_UP ) ) {
p - > start = 0 ;
} else
p - > start = 0 ;
p - > start = 0 ;
if ( p - > chan ) {
p - > chan - > _bridge = NULL ;
/* If they're dead, go ahead and hang up on the agent now */
@ -918,15 +970,21 @@ static int agent_hangup(struct ast_channel *ast)
ast_softhangup ( p - > chan , AST_SOFTHANGUP_EXPLICIT ) ;
ast_channel_unlock ( p - > chan ) ;
} else if ( p - > loginstart ) {
ast_channel_lock ( p - > chan ) ;
ast_indicate_data ( p - > chan , AST_CONTROL_HOLD ,
S_OR ( p - > moh , NULL ) ,
! ast_strlen_zero ( p - > moh ) ? strlen ( p - > moh ) + 1 : 0 ) ;
ast_channel_unlock ( p - > chan ) ;
indicate_chan = ast_channel_ref ( p - > chan ) ;
tmp_moh = ast_strdupa ( p - > moh ) ;
}
}
ast_mutex_unlock ( & p - > lock ) ;
if ( indicate_chan ) {
ast_channel_lock ( indicate_chan ) ;
ast_indicate_data ( indicate_chan , AST_CONTROL_HOLD ,
S_OR ( tmp_moh , NULL ) ,
! ast_strlen_zero ( tmp_moh ) ? strlen ( tmp_moh ) + 1 : 0 ) ;
ast_channel_unlock ( indicate_chan ) ;
indicate_chan = ast_channel_unref ( indicate_chan ) ;
}
/* Only register a device state change if the agent is still logged in */
if ( ! p - > loginstart ) {
p - > logincallerid [ 0 ] = ' \0 ' ;
@ -934,11 +992,6 @@ static int agent_hangup(struct ast_channel *ast)
ast_devstate_changed ( AST_DEVICE_NOT_INUSE , " Agent/%s " , p - > agent ) ;
}
if ( p - > pending ) {
AST_LIST_LOCK ( & agents ) ;
AST_LIST_REMOVE ( & agents , p , list ) ;
AST_LIST_UNLOCK ( & agents ) ;
}
if ( p - > abouttograb ) {
/* Let the "about to grab" thread know this isn't valid anymore, and let it
kill it later */
@ -1491,6 +1544,8 @@ static force_inline int powerof(unsigned int d)
/*!
* Lists agents and their status to the Manager API .
* It is registered on load_module ( ) and it gets called by the manager backend .
* This function locks both the pvt and the channel that owns it for a while , but
* does not keep these locks .
* \ param s
* \ param m
* \ returns
@ -1513,7 +1568,9 @@ static int action_agents(struct mansession *s, const struct message *m)
astman_send_ack ( s , m , " Agents will follow " ) ;
AST_LIST_LOCK ( & agents ) ;
AST_LIST_TRAVERSE ( & agents , p , list ) {
ast_mutex_lock ( & p - > lock ) ;
struct ast_channel * owner ;
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
/* Status Values:
AGENT_LOGGEDOFF - Agent isn ' t logged in
@ -1528,16 +1585,14 @@ static int action_agents(struct mansession *s, const struct message *m)
if ( p - > chan ) {
loginChan = ast_strdupa ( p - > chan - > name ) ;
if ( p- > owner & & p - > owner - > _bridge ) {
if ( owner & & owner - > _bridge ) {
talkingto = S_COR ( p - > chan - > caller . id . number . valid ,
p - > chan - > caller . id . number . str , " n/a " ) ;
ast_channel_lock ( p - > owner ) ;
if ( ( bridge = ast_bridged_channel ( p - > owner ) ) ) {
if ( ( bridge = ast_bridged_channel ( owner ) ) ) {
talkingtoChan = ast_strdupa ( bridge - > name ) ;
} else {
talkingtoChan = " n/a " ;
}
ast_channel_unlock ( p - > owner ) ;
status = " AGENT_ONCALL " ;
} else {
talkingto = " n/a " ;
@ -1551,6 +1606,11 @@ static int action_agents(struct mansession *s, const struct message *m)
status = " AGENT_LOGGEDOFF " ;
}
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
astman_append ( s , " Event: Agents \r \n "
" Agent: %s \r \n "
" Name: %s \r \n "
@ -1582,14 +1642,14 @@ static int agent_logoff(const char *agent, int soft)
ret = 0 ;
if ( p - > owner | | p - > chan ) {
if ( ! soft ) {
struct ast_channel * owner ;
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
while ( p - > owner & & ast_channel_trylock ( p - > owner ) ) {
DEADLOCK_AVOIDANCE ( & p - > lock ) ;
}
if ( p - > owner ) {
ast_softhangup ( p - > owner , AST_SOFTHANGUP_EXPLICIT ) ;
ast_channel_unlock ( p - > owner ) ;
if ( owner ) {
ast_softhangup ( owner , AST_SOFTHANGUP_EXPLICIT ) ;
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
while ( p - > chan & & ast_channel_trylock ( p - > chan ) ) {
@ -1726,7 +1786,9 @@ static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
AST_LIST_LOCK ( & agents ) ;
AST_LIST_TRAVERSE ( & agents , p , list ) {
struct ast_channel * owner ;
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
if ( p - > pending ) {
if ( p - > group )
ast_cli ( a - > fd , " -- Pending call to group %d \n " , powerof ( p - > group ) ) ;
@ -1739,10 +1801,11 @@ static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
username [ 0 ] = ' \0 ' ;
if ( p - > chan ) {
snprintf ( location , sizeof ( location ) , " logged in on %s " , p - > chan - > name ) ;
if ( p- > owner & & ast_bridged_channel ( p- > owner) )
if ( owner & & ast_bridged_channel ( owner) ) {
snprintf ( talkingto , sizeof ( talkingto ) , " talking to %s " , ast_bridged_channel ( p - > owner ) - > name ) ;
else
} else {
strcpy ( talkingto , " is idle " ) ;
}
online_agents + + ;
} else {
strcpy ( location , " not logged in " ) ;
@ -1755,6 +1818,11 @@ static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
username , location , talkingto , music ) ;
count_agents + + ;
}
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
ast_mutex_unlock ( & p - > lock ) ;
}
AST_LIST_UNLOCK ( & agents ) ;
@ -1795,21 +1863,32 @@ static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli
AST_LIST_LOCK ( & agents ) ;
AST_LIST_TRAVERSE ( & agents , p , list ) {
struct ast_channel * owner ;
agent_status = 0 ; /* reset it to offline */
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
if ( ! ast_strlen_zero ( p - > name ) )
snprintf ( username , sizeof ( username ) , " (%s) " , p - > name ) ;
else
username [ 0 ] = ' \0 ' ;
if ( p - > chan ) {
snprintf ( location , sizeof ( location ) , " logged in on %s " , p - > chan - > name ) ;
if ( p- > owner & & ast_bridged_channel ( p- > owner) )
snprintf ( talkingto , sizeof ( talkingto ) , " talking to %s " , ast_bridged_channel ( p- > owner) - > name ) ;
else
if ( owner & & ast_bridged_channel ( owner) ) {
snprintf ( talkingto , sizeof ( talkingto ) , " talking to %s " , ast_bridged_channel ( owner) - > name ) ;
} else {
strcpy ( talkingto , " is idle " ) ;
}
agent_status = 1 ;
online_agents + + ;
}
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
if ( ! ast_strlen_zero ( p - > moh ) )
snprintf ( music , sizeof ( music ) , " (musiconhold is '%s') " , p - > moh ) ;
if ( agent_status )
@ -2381,12 +2460,16 @@ static int agents_data_provider_get(const struct ast_data_search *search,
AST_LIST_LOCK ( & agents ) ;
AST_LIST_TRAVERSE ( & agents , p , list ) {
struct ast_channel * owner ;
data_agent = ast_data_add_node ( data_root , " agent " ) ;
if ( ! data_agent ) {
continue ;
}
ast_mutex_lock ( & p - > lock ) ;
owner = agent_lock_owner ( p ) ;
if ( ! ( p - > pending ) ) {
ast_data_add_str ( data_agent , " id " , p - > agent ) ;
ast_data_add_structure ( agent_pvt , data_agent , p ) ;
@ -2397,17 +2480,25 @@ static int agents_data_provider_get(const struct ast_data_search *search,
if ( ! data_channel ) {
ast_mutex_unlock ( & p - > lock ) ;
ast_data_remove_node ( data_root , data_agent ) ;
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
continue ;
}
ast_channel_data_add_structure ( data_channel , p - > chan , 0 ) ;
if ( p - > owner & & ast_bridged_channel ( p - > owner ) ) {
if ( owner & & ast_bridged_channel ( owner ) ) {
data_talkingto = ast_data_add_node ( data_agent , " talkingto " ) ;
if ( ! data_talkingto ) {
ast_mutex_unlock ( & p - > lock ) ;
ast_data_remove_node ( data_root , data_agent ) ;
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
continue ;
}
ast_channel_data_add_structure ( data_talkingto , ast_bridged_channel ( p - > owner ) , 0 ) ;
ast_channel_data_add_structure ( data_talkingto , ast_bridged_channel ( owner) , 0 ) ;
}
} else {
ast_data_add_node ( data_agent , " talkingto " ) ;
@ -2415,6 +2506,12 @@ static int agents_data_provider_get(const struct ast_data_search *search,
}
ast_data_add_str ( data_agent , " musiconhold " , p - > moh ) ;
}
if ( owner ) {
ast_channel_unlock ( owner ) ;
owner = ast_channel_unref ( owner ) ;
}
ast_mutex_unlock ( & p - > lock ) ;
/* if this agent doesn't match remove the added agent. */