diff --git a/channels/chan_agent.c b/channels/chan_agent.c index d9eef1c663e91ec9d9cb35b2d0b48e435b896bec..61f5bb29b492e4b27b506f22704d11c05d967d32 100755 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -52,11 +52,13 @@ static char *app = "AgentLogin"; static char *synopsis = "Call agent login"; static char *descrip = -" AgentLogin():\n" +" AgentLogin([AgentNo][|options]):\n" "Asks the agent to login to the system. Always returns -1. While\n" "logged in, the agent can receive calls and will hear a 'beep'\n" "when a new call comes in. The agent can dump the call by pressing\n" -"the star key.\n"; +"the star key.\n" +"The option string may contain zero or more of the following characters:\n" +" 's' -- silent login - do not announce the login ok segment\n"; static char moh[80] = "default"; @@ -77,6 +79,9 @@ static struct agent_pvt { char agent[AST_MAX_AGENT]; /* Agent ID */ char password[AST_MAX_AGENT]; /* Password for Agent login */ char name[AST_MAX_AGENT]; + pthread_mutex_t app_lock; /* Synchronization between owning applications */ + volatile pthread_t owning_app; /* Owning application thread id */ + volatile int app_sleep_cond; /* Sleep condition for the login app */ struct ast_channel *owner; /* Agent */ struct ast_channel *chan; /* Channel we use */ struct agent_pvt *next; /* Agent */ @@ -119,6 +124,10 @@ static int add_agent(struct ast_variable *var) if (p) { memset(p, 0, sizeof(struct agent_pvt)); strncpy(p->agent, tmp, sizeof(p->agent) -1); + ast_pthread_mutex_init( &p->lock ); + ast_pthread_mutex_init( &p->app_lock ); + p->owning_app = -1; + p->app_sleep_cond = 1; p->next = agents; agents = p; @@ -179,6 +188,7 @@ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) ast_pthread_mutex_lock(&p->lock); if (p->owner != oldchan) { ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); + ast_pthread_mutex_unlock(&p->lock); return -1; } p->owner = newchan; @@ -228,6 +238,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout) } /* Call is immediately up */ ast_setstate(ast, AST_STATE_UP); + CLEANUP(ast,p); ast_pthread_mutex_unlock(&p->lock); return res; } @@ -238,21 +249,43 @@ static int agent_hangup(struct ast_channel *ast) ast_pthread_mutex_lock(&p->lock); p->owner = NULL; ast->pvt->pvt = NULL; + p->app_sleep_cond = 1; ast_pthread_mutex_unlock(&p->lock); + /* Release ownership of the agent to other threads (presumably running the login app). */ + ast_pthread_mutex_unlock(&p->app_lock); if (p->chan) { /* If they're dead, go ahead and hang up on the agent now */ + ast_pthread_mutex_lock(&p->chan->lock); if (p->dead) ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); ast_moh_start(p->chan, p->moh); - } else if (p->dead) + ast_pthread_mutex_unlock(&p->chan->lock); + } else if (p->dead) /* Go ahead and lose it */ free(p); + return 0; } +static int agent_cont_sleep( void *data ) +{ + struct agent_pvt *p; + int res; + + p = (struct agent_pvt *)data; + + ast_pthread_mutex_lock(&p->lock); + res = p->app_sleep_cond; + ast_pthread_mutex_unlock(&p->lock); + if( !res ) + ast_log( LOG_DEBUG, "agent_cont_sleep() returning %d\n", res ); + return res; +} + static struct ast_channel *agent_new(struct agent_pvt *p, int state) { struct ast_channel *tmp; + struct ast_frame null_frame = { AST_FRAME_NULL }; if (!p->chan) { ast_log(LOG_WARNING, "No channel? :(\n"); return NULL; @@ -286,12 +319,26 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state) strncpy(tmp->context, p->chan->context, sizeof(tmp->context)-1); strncpy(tmp->exten, p->chan->exten, sizeof(tmp->exten)-1); tmp->priority = 1; - /* Wake up any waiting blockers (by definition the login app) */ + /* 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; + if( pthread_mutex_trylock(&p->app_lock) ) + { + ast_queue_frame(p->chan, &null_frame, 1); + ast_pthread_mutex_unlock(&p->lock); /* For other thread to read the condition. */ + ast_pthread_mutex_lock(&p->app_lock); + ast_pthread_mutex_lock(&p->lock); + } + p->owning_app = pthread_self(); + /* After the above step, there should not be any blockers. */ if (p->chan->blocking) { - pthread_kill(p->chan->blocker, SIGURG); - /* Wait until the blocker releases it */ - while(p->chan->blocking) - usleep(1000); + ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" ); + CRASH; } ast_moh_stop(p->chan); } else @@ -435,11 +482,35 @@ static int login_exec(struct ast_channel *chan, void *data) char pass[AST_MAX_AGENT]; char xpass[AST_MAX_AGENT] = ""; char *errmsg; + char info[512]; + char *opt_user = NULL; + char *options = NULL; + int play_announcement; + struct timespec required; + struct timespec remaining; + int delay; + LOCAL_USER_ADD(u); + + /* Parse the arguments XXX Check for failure XXX */ + strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1); + opt_user = info; + if( opt_user ) { + options = strchr(opt_user, '|'); + if (options) { + *options = '\0'; + options++; + } + } + if (chan->_state != AST_STATE_UP) res = ast_answer(chan); - if (!res) - res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); + if (!res) { + if( opt_user ) + strncpy( user, opt_user, AST_MAX_AGENT ); + else + res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); + } while (!res && (tries < 3)) { /* Check for password */ ast_pthread_mutex_lock(&agentlock); @@ -451,7 +522,7 @@ static int login_exec(struct ast_channel *chan, void *data) } ast_pthread_mutex_unlock(&agentlock); if (!res) { - if (strlen(xpass) || !p) + if (strlen(xpass)) res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0); else strcpy(pass, ""); @@ -470,7 +541,12 @@ static int login_exec(struct ast_channel *chan, void *data) if (!strcmp(p->agent, user) && !strcmp(p->password, pass)) { if (!p->chan) { - res = ast_streamfile(chan, "agent-loginok", chan->language); + play_announcement = 1; + if( options ) + if( strchr( options, 's' ) ) + play_announcement = 0; + if( play_announcement ) + res = ast_streamfile(chan, "agent-loginok", chan->language); if (!res) ast_waitstream(chan, ""); if (!res) { @@ -500,13 +576,33 @@ static int login_exec(struct ast_channel *chan, void *data) p->chan = chan; ast_pthread_mutex_unlock(&p->lock); ast_pthread_mutex_unlock(&agentlock); - while ((res >= 0) && (p->chan == chan)) { - /* True sleep here, since we're being monitored - elsewhere instead */ + while (res >= 0) { + /* If we are not the owner, delay here for a while + * so other interested threads can kick in. */ + delay = 0; + ast_pthread_mutex_lock(&p->lock); + if (p->chan != chan) + res = -1; if (p->owner) - sleep(1); - else - res = ast_safe_sleep(chan, 1000); + delay = 1; + ast_pthread_mutex_unlock(&p->lock); + if (delay) { + sched_yield(); + required.tv_sec = 0; + required.tv_nsec = 20 * 1000 * 1000; + nanosleep( &required, &remaining ); + } + if (res) + break; + + /* Synchronize channel ownership between call to agent and itself. */ + pthread_mutex_lock( &p->app_lock ); + ast_pthread_mutex_lock(&p->lock); + p->owning_app = pthread_self(); + ast_pthread_mutex_unlock(&p->lock); + res = ast_safe_sleep_conditional( chan, 1000, + agent_cont_sleep, p ); + pthread_mutex_unlock( &p->app_lock ); } if (res && p->owner) ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); @@ -522,6 +618,10 @@ static int login_exec(struct ast_channel *chan, void *data) if (p->dead && !p->owner) free(p); } + else { + ast_pthread_mutex_unlock(&p->lock); + p = NULL; + } res = -1; } else { ast_pthread_mutex_unlock(&p->lock);