From 1cfde1088c313e0f0c4979cab4a307e1fce3fa13 Mon Sep 17 00:00:00 2001 From: Matthew Nicholson Date: Fri, 22 Apr 2011 20:01:48 +0000 Subject: [PATCH] Prevent the login thread and the app threads from using the asterisk channel at the same time. ABE-2756 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@314908 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_agent.c | 169 +++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 78 deletions(-) diff --git a/channels/chan_agent.c b/channels/chan_agent.c index e027527ad6..65b4d84a09 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -194,9 +194,9 @@ struct agent_pvt { char password[AST_MAX_AGENT]; /*!< Password for Agent login */ char name[AST_MAX_AGENT]; int inherited_devicestate; /*!< Does the underlying channel have a devicestate to pass? */ - ast_mutex_t app_lock; /**< Synchronization between owning applications */ int app_lock_flag; ast_cond_t app_complete_cond; + ast_cond_t login_wait_cond; volatile int app_sleep_cond; /**< Sleep condition for the login app */ struct ast_channel *owner; /**< Agent */ /**! channel they logged in from. This may also be used to tell if an agent @@ -384,8 +384,8 @@ static struct agent_pvt *add_agent(char *agent, int pending) return NULL; ast_copy_string(p->agent, agt, sizeof(p->agent)); ast_mutex_init(&p->lock); - ast_mutex_init(&p->app_lock); ast_cond_init(&p->app_complete_cond, NULL); + ast_cond_init(&p->login_wait_cond, NULL); p->app_lock_flag = 0; p->app_sleep_cond = 1; p->group = group; @@ -429,21 +429,26 @@ static struct agent_pvt *add_agent(char *agent, int pending) */ static int agent_cleanup(struct agent_pvt *p) { - struct ast_channel *chan = p->owner; + struct ast_channel *chan = NULL; + ast_mutex_lock(&p->lock); + chan = p->owner; p->owner = NULL; chan->tech_pvt = NULL; - p->app_sleep_cond = 1; /* Release ownership of the agent to other threads (presumably running the login app). */ + p->app_sleep_cond = 1; p->app_lock_flag = 0; ast_cond_signal(&p->app_complete_cond); if (chan) ast_channel_free(chan); if (p->dead) { + ast_mutex_unlock(&p->lock); ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); ast_cond_destroy(&p->app_complete_cond); + ast_cond_destroy(&p->login_wait_cond); free(p); - } + } else { + ast_mutex_unlock(&p->lock); + } return 0; } @@ -752,22 +757,24 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout) int res = -1; int newstate=0; struct ast_channel *chan; + ast_mutex_lock(&p->lock); p->acknowledged = 0; + + if (p->pending) { + ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); + ast_mutex_unlock(&p->lock); + ast_setstate(ast, AST_STATE_DIALING); + return 0; + } + if (!p->chan) { - if (p->pending) { - ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); - newstate = AST_STATE_DIALING; - res = 0; - } else { - ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n"); - res = -1; - } + ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n"); ast_mutex_unlock(&p->lock); - if (newstate) - ast_setstate(ast, newstate); return res; - } else if (!ast_strlen_zero(p->loginchan)) { + } + + if (!ast_strlen_zero(p->loginchan)) { time(&p->start); /* Call on this agent */ if (option_verbose > 2) @@ -798,8 +805,8 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout) } ast_mutex_lock(&p->lock); - if (p->chan != chan) { - /* chan went away while we were streaming */ + if (!p->chan) { + /* chan went away while we were streaming, this shouldn't be possible */ res = -1; } @@ -900,6 +907,12 @@ static int agent_hangup(struct ast_channel *ast) p->app_sleep_cond = 1; p->acknowledged = 0; + /* Release ownership of the agent to other threads (presumably running the login app). */ + if (ast_strlen_zero(p->loginchan)) { + p->app_lock_flag = 0; + ast_cond_signal(&p->app_complete_cond); + } + /* if they really are hung up then set start to 0 so the test * later if we're called on an already downed channel * doesn't cause an agent to be logged out like when @@ -985,8 +998,8 @@ static int agent_hangup(struct ast_channel *ast) p->abouttograb = 0; } else if (p->dead) { ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); ast_cond_destroy(&p->app_complete_cond); + ast_cond_destroy(&p->login_wait_cond); free(p); } else { if (p->chan) { @@ -996,11 +1009,6 @@ static int agent_hangup(struct ast_channel *ast) p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); ast_mutex_unlock(&p->lock); } - /* Release ownership of the agent to other threads (presumably running the login app). */ - if (ast_strlen_zero(p->loginchan)) { - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - } } return 0; } @@ -1088,7 +1096,6 @@ static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct static struct ast_channel *agent_new(struct agent_pvt *p, int state) { struct ast_channel *tmp; - int alreadylocked; #if 0 if (!p->chan) { ast_log(LOG_WARNING, "No channel? :(\n"); @@ -1131,50 +1138,6 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state) #endif ast_update_use_count(); tmp->priority = 1; - /* Wake up and wait for other applications (by definition the login app) - * to release this channel). Takes ownership of the agent channel - * to this thread only. - * For signalling the other thread, ast_queue_frame is used until we - * can safely use signals for this purpose. The pselect() needs to be - * implemented in the kernel for this. - */ - p->app_sleep_cond = 0; - - alreadylocked = p->app_lock_flag; - p->app_lock_flag = 1; - - if(ast_strlen_zero(p->loginchan) && alreadylocked) { - if (p->chan) { - ast_queue_frame(p->chan, &ast_null_frame); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - p->app_lock_flag = 1; - ast_mutex_lock(&p->lock); - } else { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - return NULL; - } - } else if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_queue_frame(p->chan, &ast_null_frame); - if (!p->chan) { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - return NULL; - } - } - if (p->chan) - ast_indicate(p->chan, AST_CONTROL_UNHOLD); return tmp; } @@ -1318,8 +1281,8 @@ static int read_agent_config(void) if (!p->owner) { if (!p->chan) { ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); ast_cond_destroy(&p->app_complete_cond); + ast_cond_destroy(&p->login_wait_cond); free(p); } else { /* Cause them to hang up */ @@ -1562,6 +1525,47 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat } *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED; AST_LIST_UNLOCK(&agents); + + if (chan) { + ast_mutex_lock(&p->lock); + if (p->pending) { + ast_mutex_unlock(&p->lock); + return chan; + } + + if (!p->chan) { + ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n"); + *cause = AST_CAUSE_UNREGISTERED; + ast_mutex_unlock(&p->lock); + agent_hangup(chan); + return NULL; + } + + /* when not in callback mode we need to take control of the channel + * from the login app thread */ + if(ast_strlen_zero(p->loginchan)) { + p->app_sleep_cond = 0; + p->app_lock_flag = 1; + + ast_queue_frame(p->chan, &ast_null_frame); + ast_cond_wait(&p->login_wait_cond, &p->lock); + + if (!p->chan) { + ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n"); + p->app_sleep_cond = 1; + p->app_lock_flag = 0; + ast_cond_signal(&p->app_complete_cond); + ast_mutex_unlock(&p->lock); + *cause = AST_CAUSE_UNREGISTERED; + agent_hangup(chan); + return NULL; + } + + ast_indicate(p->chan, AST_CONTROL_UNHOLD); + } + ast_mutex_unlock(&p->lock); + } + return chan; } @@ -2314,14 +2318,15 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode) } ast_mutex_unlock(&p->lock); AST_LIST_UNLOCK(&agents); + /* Synchronize channel ownership between call to agent and itself. */ - ast_mutex_lock(&p->app_lock); + ast_mutex_lock(&p->lock); if (p->app_lock_flag == 1) { - ast_cond_wait(&p->app_complete_cond, &p->app_lock); + ast_cond_signal(&p->login_wait_cond); + ast_cond_wait(&p->app_complete_cond, &p->lock); } - ast_mutex_unlock(&p->app_lock); - ast_mutex_lock(&p->lock); ast_mutex_unlock(&p->lock); + if (p->ackcall > 1) res = agent_ack_sleep(p); else @@ -2337,13 +2342,21 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode) sched_yield(); } ast_mutex_lock(&p->lock); - if (res && p->owner) - ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); /* Log us off if appropriate */ if (p->chan == chan) { p->chan = NULL; p->inherited_devicestate = -1; } + + /* Synchronize channel ownership between call to agent and itself. */ + if (p->app_lock_flag == 1) { + ast_cond_signal(&p->login_wait_cond); + ast_cond_wait(&p->app_complete_cond, &p->lock); + } + + if (res && p->owner) + ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); + p->acknowledged = 0; logintime = time(NULL) - p->loginstart; p->loginstart = 0; @@ -2360,8 +2373,8 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode) ast_device_state_changed("Agent/%s", p->agent); if (p->dead && !p->owner) { ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); ast_cond_destroy(&p->app_complete_cond); + ast_cond_destroy(&p->login_wait_cond); free(p); } } -- 2.47.2