mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-01 19:43:03 +00:00
chan_agent: Prevent multiple channels from logging in as the same agent.
Multiple channels logging in as the same agent can result in dead channels waiting for a condition signal that will never come because another channel thread stole it. A symptom is chan_sip repeatedly generating warning messages about rescheduling autodestruction of dialogs with an agent channel owner. * Made only login_exec() (the app AgentLogin) clear the agent_pvt->chan pointer to prevent multiple channels from logging in as the same agent. agent_read(), agent_call(), and agent_set_base_channel() no longer disconnect the agent channel from the agent_pvt. This also eliminates the need to keep checking for agent_pvt->chan being NULL. * Made agent_hangup() not wake up the AgentLogin agent thread until it is done. * Made agent_request() not able to get the agent until he has logged in and any wrapup time has expired. * Made agent_request() use ast_hangup() instead of agent_hangup() to correctly dispose of a channel. * Removed agent_set_base_channel(). Nobody calls it and it is a bad thing in general. * Made only agent_devicestate() determine the current device state of an agent. Note: Agent group device states have never been supported. Review: https://reviewboard.asterisk.org/r/2260/ ........ Merged revisions 380364 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 380384 from http://svn.asterisk.org/svn/asterisk/branches/11 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@380386 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -344,7 +344,6 @@ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
|
|||||||
static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
|
static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
|
||||||
static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
|
static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
|
||||||
static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
|
static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
|
||||||
static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
|
|
||||||
static int agent_logoff(const char *agent, int soft);
|
static int agent_logoff(const char *agent, int soft);
|
||||||
|
|
||||||
/*! \brief Channel interface description for PBX integration */
|
/*! \brief Channel interface description for PBX integration */
|
||||||
@@ -368,7 +367,6 @@ static struct ast_channel_tech agent_tech = {
|
|||||||
.fixup = agent_fixup,
|
.fixup = agent_fixup,
|
||||||
.bridged_channel = agent_bridgedchannel,
|
.bridged_channel = agent_bridgedchannel,
|
||||||
.get_base_channel = agent_get_base_channel,
|
.get_base_channel = agent_get_base_channel,
|
||||||
.set_base_channel = agent_set_base_channel,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -625,17 +623,10 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
|
|||||||
ast_copy_flags(ast_channel_flags(p->chan), ast_channel_flags(ast), AST_FLAG_EXCEPTION);
|
ast_copy_flags(ast_channel_flags(p->chan), ast_channel_flags(ast), AST_FLAG_EXCEPTION);
|
||||||
ast_channel_fdno_set(p->chan, (ast_channel_fdno(ast) == AST_AGENT_FD) ? AST_TIMING_FD : ast_channel_fdno(ast));
|
ast_channel_fdno_set(p->chan, (ast_channel_fdno(ast) == AST_AGENT_FD) ? AST_TIMING_FD : ast_channel_fdno(ast));
|
||||||
f = ast_read(p->chan);
|
f = ast_read(p->chan);
|
||||||
|
ast_channel_fdno_set(ast, -1);
|
||||||
} else
|
} else
|
||||||
f = &ast_null_frame;
|
f = &ast_null_frame;
|
||||||
if (!f) {
|
if (f) {
|
||||||
/* If there's a channel, make it NULL */
|
|
||||||
if (p->chan) {
|
|
||||||
ast_channel_internal_bridged_channel_set(p->chan, NULL);
|
|
||||||
p->chan = NULL;
|
|
||||||
ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
|
||||||
p->acknowledged = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* if acknowledgement is not required, and the channel is up, we may have missed
|
/* if acknowledgement is not required, and the channel is up, we may have missed
|
||||||
an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
|
an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
|
||||||
if (!p->ackcall && !p->acknowledged && p->chan && (ast_channel_state(p->chan) == AST_STATE_UP)) {
|
if (!p->ackcall && !p->acknowledged && p->chan && (ast_channel_state(p->chan) == AST_STATE_UP)) {
|
||||||
@@ -840,9 +831,8 @@ static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int dur
|
|||||||
static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
|
static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
|
||||||
{
|
{
|
||||||
struct agent_pvt *p = ast_channel_tech_pvt(ast);
|
struct agent_pvt *p = ast_channel_tech_pvt(ast);
|
||||||
int res = -1;
|
int res;
|
||||||
int newstate=0;
|
int newstate=0;
|
||||||
struct ast_channel *chan;
|
|
||||||
|
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
p->acknowledged = 0;
|
p->acknowledged = 0;
|
||||||
@@ -854,29 +844,20 @@ static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p->chan) {
|
ast_assert(p->chan != NULL);
|
||||||
ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
|
|
||||||
ast_mutex_unlock(&p->lock);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, ast_channel_name(p->chan));
|
ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, ast_channel_name(p->chan));
|
||||||
ast_debug(3, "Playing beep, lang '%s'\n", ast_channel_language(p->chan));
|
ast_debug(3, "Playing beep, lang '%s'\n", ast_channel_language(p->chan));
|
||||||
|
|
||||||
chan = p->chan;
|
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
|
|
||||||
res = ast_streamfile(chan, beep, ast_channel_language(chan));
|
res = ast_streamfile(p->chan, beep, ast_channel_language(p->chan));
|
||||||
ast_debug(3, "Played beep, result '%d'\n", res);
|
ast_debug(3, "Played beep, result '%d'\n", res);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
res = ast_waitstream(chan, "");
|
res = ast_waitstream(p->chan, "");
|
||||||
ast_debug(3, "Waited for stream, result '%d'\n", res);
|
ast_debug(3, "Waited for stream, result '%d'\n", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (!p->chan) {
|
|
||||||
/* chan went away while we were streaming, this shouldn't be possible */
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
struct ast_format tmpfmt;
|
struct ast_format tmpfmt;
|
||||||
@@ -884,10 +865,6 @@ static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
|
|||||||
ast_debug(3, "Set read format, result '%d'\n", res);
|
ast_debug(3, "Set read format, result '%d'\n", res);
|
||||||
if (res)
|
if (res)
|
||||||
ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(&tmpfmt));
|
ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(&tmpfmt));
|
||||||
} else {
|
|
||||||
/* Agent hung-up */
|
|
||||||
p->chan = NULL;
|
|
||||||
ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@@ -912,7 +889,7 @@ static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
|
|||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
if (newstate)
|
if (newstate)
|
||||||
ast_setstate(ast, newstate);
|
ast_setstate(ast, newstate);
|
||||||
return res;
|
return res ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
|
/*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
|
||||||
@@ -932,23 +909,6 @@ struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
|
|||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
|
|
||||||
{
|
|
||||||
struct agent_pvt *p = NULL;
|
|
||||||
|
|
||||||
if (!chan || !base) {
|
|
||||||
ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p = ast_channel_tech_pvt(chan);
|
|
||||||
if (!p) {
|
|
||||||
ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", ast_channel_name(chan));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p->chan = base;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int agent_hangup(struct ast_channel *ast)
|
static int agent_hangup(struct ast_channel *ast)
|
||||||
{
|
{
|
||||||
struct agent_pvt *p = ast_channel_tech_pvt(ast);
|
struct agent_pvt *p = ast_channel_tech_pvt(ast);
|
||||||
@@ -964,13 +924,8 @@ static int agent_hangup(struct ast_channel *ast)
|
|||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
p->owner = NULL;
|
p->owner = NULL;
|
||||||
ast_channel_tech_pvt_set(ast, NULL);
|
ast_channel_tech_pvt_set(ast, NULL);
|
||||||
p->app_sleep_cond = 1;
|
|
||||||
p->acknowledged = 0;
|
p->acknowledged = 0;
|
||||||
|
|
||||||
/* Release ownership of the agent to other threads (presumably running the login app). */
|
|
||||||
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
|
/* if they really are hung up then set start to 0 so the test
|
||||||
* later if we're called on an already downed channel
|
* later if we're called on an already downed channel
|
||||||
* doesn't cause an agent to be logged out like when
|
* doesn't cause an agent to be logged out like when
|
||||||
@@ -999,26 +954,26 @@ static int agent_hangup(struct ast_channel *ast)
|
|||||||
indicate_chan = ast_channel_unref(indicate_chan);
|
indicate_chan = ast_channel_unref(indicate_chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only register a device state change if the agent is still logged in */
|
ast_mutex_lock(&p->lock);
|
||||||
if (p->loginstart) {
|
|
||||||
ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->abouttograb) {
|
if (p->abouttograb) {
|
||||||
/* Let the "about to grab" thread know this isn't valid anymore, and let it
|
/* Let the "about to grab" thread know this isn't valid anymore, and let it
|
||||||
kill it later */
|
kill it later */
|
||||||
p->abouttograb = 0;
|
p->abouttograb = 0;
|
||||||
} else if (p->dead) {
|
} else if (p->dead) {
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
agent_pvt_destroy(p);
|
agent_pvt_destroy(p);
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (p->chan) {
|
/* Store last disconnect time */
|
||||||
/* Not dead -- check availability now */
|
p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
|
||||||
ast_mutex_lock(&p->lock);
|
|
||||||
/* Store last disconnect time */
|
|
||||||
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). */
|
||||||
|
p->app_sleep_cond = 1;
|
||||||
|
p->app_lock_flag = 0;
|
||||||
|
ast_cond_signal(&p->app_complete_cond);
|
||||||
|
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1450,7 +1405,8 @@ static struct ast_channel *agent_request(const char *type, struct ast_format_cap
|
|||||||
hasagent++;
|
hasagent++;
|
||||||
}
|
}
|
||||||
now = ast_tvnow();
|
now = ast_tvnow();
|
||||||
if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
|
if (p->loginstart
|
||||||
|
&& (!p->lastdisc.tv_sec || ast_tvdiff_ms(now, p->lastdisc) > 0)) {
|
||||||
p->lastdisc = ast_tv(0, 0);
|
p->lastdisc = ast_tv(0, 0);
|
||||||
/* Agent must be registered, but not have any active call, and not be in a waiting state */
|
/* Agent must be registered, but not have any active call, and not be in a waiting state */
|
||||||
if (!p->owner && p->chan) {
|
if (!p->owner && p->chan) {
|
||||||
@@ -1499,10 +1455,10 @@ static struct ast_channel *agent_request(const char *type, struct ast_format_cap
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!p->chan) {
|
if (!p->chan) {
|
||||||
ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
|
ast_debug(1, "Agent disconnected before we could connect the call\n");
|
||||||
*cause = AST_CAUSE_UNREGISTERED;
|
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
agent_hangup(chan);
|
ast_hangup(chan);
|
||||||
|
*cause = AST_CAUSE_UNREGISTERED;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1510,18 +1466,14 @@ static struct ast_channel *agent_request(const char *type, struct ast_format_cap
|
|||||||
* thread */
|
* thread */
|
||||||
p->app_sleep_cond = 0;
|
p->app_sleep_cond = 0;
|
||||||
p->app_lock_flag = 1;
|
p->app_lock_flag = 1;
|
||||||
|
|
||||||
ast_queue_frame(p->chan, &ast_null_frame);
|
ast_queue_frame(p->chan, &ast_null_frame);
|
||||||
ast_cond_wait(&p->login_wait_cond, &p->lock);
|
ast_cond_wait(&p->login_wait_cond, &p->lock);
|
||||||
|
|
||||||
if (!p->chan) {
|
if (!p->chan) {
|
||||||
ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
|
ast_debug(1, "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);
|
ast_mutex_unlock(&p->lock);
|
||||||
|
ast_hangup(chan);
|
||||||
*cause = AST_CAUSE_UNREGISTERED;
|
*cause = AST_CAUSE_UNREGISTERED;
|
||||||
agent_hangup(chan);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1933,10 +1885,9 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
int tries = 0;
|
int tries = 0;
|
||||||
int max_login_tries = maxlogintries;
|
int max_login_tries = maxlogintries;
|
||||||
struct agent_pvt *p;
|
struct agent_pvt *p;
|
||||||
char user[AST_MAX_AGENT] = "";
|
char user[AST_MAX_AGENT];
|
||||||
char pass[AST_MAX_AGENT];
|
char pass[AST_MAX_AGENT];
|
||||||
char agent[AST_MAX_AGENT] = "";
|
char xpass[AST_MAX_AGENT];
|
||||||
char xpass[AST_MAX_AGENT] = "";
|
|
||||||
char *errmsg;
|
char *errmsg;
|
||||||
char *parse;
|
char *parse;
|
||||||
AST_DECLARE_APP_ARGS(args,
|
AST_DECLARE_APP_ARGS(args,
|
||||||
@@ -1948,7 +1899,9 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
int play_announcement = 1;
|
int play_announcement = 1;
|
||||||
char agent_goodbye[AST_MAX_FILENAME_LEN];
|
char agent_goodbye[AST_MAX_FILENAME_LEN];
|
||||||
int update_cdr = updatecdr;
|
int update_cdr = updatecdr;
|
||||||
char *filename = "agent-loginok";
|
|
||||||
|
user[0] = '\0';
|
||||||
|
xpass[0] = '\0';
|
||||||
|
|
||||||
parse = ast_strdupa(data);
|
parse = ast_strdupa(data);
|
||||||
|
|
||||||
@@ -2020,15 +1973,12 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
AST_LIST_LOCK(&agents);
|
AST_LIST_LOCK(&agents);
|
||||||
AST_LIST_TRAVERSE(&agents, p, list) {
|
AST_LIST_TRAVERSE(&agents, p, list) {
|
||||||
int unlock_channel = 1;
|
int unlock_channel = 1;
|
||||||
|
|
||||||
ast_channel_lock(chan);
|
ast_channel_lock(chan);
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (!strcmp(p->agent, user) &&
|
if (!strcmp(p->agent, user) &&
|
||||||
!strcmp(p->password, pass) && !p->pending) {
|
!strcmp(p->password, pass) && !p->pending) {
|
||||||
|
|
||||||
/* Ensure we can't be gotten until we're done */
|
|
||||||
p->lastdisc = ast_tvnow();
|
|
||||||
p->lastdisc.tv_sec++;
|
|
||||||
|
|
||||||
/* Set Channel Specific Agent Overrides */
|
/* Set Channel Specific Agent Overrides */
|
||||||
if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
|
if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
|
||||||
if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
|
if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
|
||||||
@@ -2077,20 +2027,13 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
ast_channel_unlock(chan);
|
ast_channel_unlock(chan);
|
||||||
unlock_channel = 0;
|
unlock_channel = 0;
|
||||||
/* End Channel Specific Agent Overrides */
|
/* End Channel Specific Agent Overrides */
|
||||||
|
|
||||||
if (!p->chan) {
|
if (!p->chan) {
|
||||||
long logintime;
|
/* Ensure nobody else can be this agent until we're done. */
|
||||||
snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
|
p->chan = chan;
|
||||||
|
|
||||||
p->acknowledged = 0;
|
p->acknowledged = 0;
|
||||||
|
|
||||||
ast_mutex_unlock(&p->lock);
|
|
||||||
AST_LIST_UNLOCK(&agents);
|
|
||||||
if( !res && play_announcement==1 )
|
|
||||||
res = ast_streamfile(chan, filename, ast_channel_language(chan));
|
|
||||||
if (!res)
|
|
||||||
ast_waitstream(chan, "");
|
|
||||||
AST_LIST_LOCK(&agents);
|
|
||||||
ast_mutex_lock(&p->lock);
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
struct ast_format tmpfmt;
|
struct ast_format tmpfmt;
|
||||||
res = ast_set_read_format_from_cap(chan, ast_channel_nativeformats(chan));
|
res = ast_set_read_format_from_cap(chan, ast_channel_nativeformats(chan));
|
||||||
@@ -2105,15 +2048,32 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(&tmpfmt));
|
ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(&tmpfmt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Check once more just in case */
|
if (!res && play_announcement == 1) {
|
||||||
if (p->chan)
|
ast_mutex_unlock(&p->lock);
|
||||||
res = -1;
|
AST_LIST_UNLOCK(&agents);
|
||||||
|
res = ast_streamfile(chan, "agent-loginok", ast_channel_language(chan));
|
||||||
|
if (!res) {
|
||||||
|
ast_waitstream(chan, "");
|
||||||
|
}
|
||||||
|
AST_LIST_LOCK(&agents);
|
||||||
|
ast_mutex_lock(&p->lock);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
long logintime;
|
||||||
|
char agent[AST_MAX_AGENT];
|
||||||
|
|
||||||
|
snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
|
||||||
|
|
||||||
|
/* Login this channel and wait for it to go away */
|
||||||
ast_indicate_data(chan, AST_CONTROL_HOLD,
|
ast_indicate_data(chan, AST_CONTROL_HOLD,
|
||||||
S_OR(p->moh, NULL),
|
S_OR(p->moh, NULL),
|
||||||
!ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
|
!ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
|
||||||
if (p->loginstart == 0)
|
|
||||||
time(&p->loginstart);
|
/* Must be done after starting HOLD. */
|
||||||
|
p->lastdisc = ast_tvnow();
|
||||||
|
time(&p->loginstart);
|
||||||
|
|
||||||
/*** DOCUMENTATION
|
/*** DOCUMENTATION
|
||||||
<managerEventInstance>
|
<managerEventInstance>
|
||||||
<synopsis>Raised when an Agent has logged in.</synopsis>
|
<synopsis>Raised when an Agent has logged in.</synopsis>
|
||||||
@@ -2134,41 +2094,31 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
"Uniqueid: %s\r\n",
|
"Uniqueid: %s\r\n",
|
||||||
p->agent, ast_channel_name(chan), ast_channel_uniqueid(chan));
|
p->agent, ast_channel_name(chan), ast_channel_uniqueid(chan));
|
||||||
if (update_cdr && ast_channel_cdr(chan))
|
if (update_cdr && ast_channel_cdr(chan))
|
||||||
snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "Agent/%s", p->agent);
|
snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "%s", agent);
|
||||||
ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGIN", "%s", ast_channel_name(chan));
|
ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGIN", "%s", ast_channel_name(chan));
|
||||||
ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
|
ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
|
||||||
ast_getformatname(ast_channel_readformat(chan)), ast_getformatname(ast_channel_writeformat(chan)));
|
ast_getformatname(ast_channel_readformat(chan)), ast_getformatname(ast_channel_writeformat(chan)));
|
||||||
/* Login this channel and wait for it to go away */
|
|
||||||
p->chan = chan;
|
|
||||||
if (p->ackcall) {
|
|
||||||
check_beep(p, 0);
|
|
||||||
} else {
|
|
||||||
check_availability(p, 0);
|
|
||||||
}
|
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
AST_LIST_UNLOCK(&agents);
|
AST_LIST_UNLOCK(&agents);
|
||||||
ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (p->deferlogoff && p->chan) {
|
if (p->deferlogoff) {
|
||||||
ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
|
|
||||||
p->deferlogoff = 0;
|
p->deferlogoff = 0;
|
||||||
}
|
ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
|
||||||
if (p->chan != chan)
|
ast_mutex_unlock(&p->lock);
|
||||||
res = -1;
|
|
||||||
ast_mutex_unlock(&p->lock);
|
|
||||||
/* Yield here so other interested threads can kick in. */
|
|
||||||
sched_yield();
|
|
||||||
if (res)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
|
|
||||||
AST_LIST_LOCK(&agents);
|
AST_LIST_LOCK(&agents);
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (p->lastdisc.tv_sec) {
|
if (p->lastdisc.tv_sec) {
|
||||||
if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
|
if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
|
||||||
ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
|
ast_debug(1, "Wrapup time for %s expired!\n", agent);
|
||||||
p->lastdisc = ast_tv(0, 0);
|
p->lastdisc = ast_tv(0, 0);
|
||||||
ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent);
|
||||||
if (p->ackcall) {
|
if (p->ackcall) {
|
||||||
check_beep(p, 0);
|
check_beep(p, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -2179,47 +2129,53 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
AST_LIST_UNLOCK(&agents);
|
AST_LIST_UNLOCK(&agents);
|
||||||
|
|
||||||
/* Synchronize channel ownership between call to agent and itself. */
|
/* Synchronize channel ownership between call to agent and itself. */
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (p->app_lock_flag == 1) {
|
if (p->app_lock_flag) {
|
||||||
ast_cond_signal(&p->login_wait_cond);
|
ast_cond_signal(&p->login_wait_cond);
|
||||||
ast_cond_wait(&p->app_complete_cond, &p->lock);
|
ast_cond_wait(&p->app_complete_cond, &p->lock);
|
||||||
|
if (ast_check_hangup(chan)) {
|
||||||
|
/* Agent hungup */
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
|
|
||||||
if (p->ackcall) {
|
if (p->ackcall) {
|
||||||
res = agent_ack_sleep(p);
|
res = agent_ack_sleep(p);
|
||||||
|
if (res == 1) {
|
||||||
|
AST_LIST_LOCK(&agents);
|
||||||
|
ast_mutex_lock(&p->lock);
|
||||||
|
check_availability(p, 0);
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
|
AST_LIST_UNLOCK(&agents);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
|
res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
|
||||||
}
|
}
|
||||||
if (p->ackcall && (res == 1)) {
|
|
||||||
AST_LIST_LOCK(&agents);
|
|
||||||
ast_mutex_lock(&p->lock);
|
|
||||||
check_availability(p, 0);
|
|
||||||
ast_mutex_unlock(&p->lock);
|
|
||||||
AST_LIST_UNLOCK(&agents);
|
|
||||||
res = 0;
|
|
||||||
}
|
|
||||||
sched_yield();
|
|
||||||
}
|
}
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
/* Log us off if appropriate */
|
|
||||||
if (p->chan == chan) {
|
/* Logoff this channel */
|
||||||
p->chan = NULL;
|
p->chan = NULL;
|
||||||
}
|
logintime = time(NULL) - p->loginstart;
|
||||||
|
p->loginstart = 0;
|
||||||
|
|
||||||
/* Synchronize channel ownership between call to agent and itself. */
|
/* Synchronize channel ownership between call to agent and itself. */
|
||||||
if (p->app_lock_flag == 1) {
|
if (p->app_lock_flag) {
|
||||||
ast_cond_signal(&p->login_wait_cond);
|
ast_cond_signal(&p->login_wait_cond);
|
||||||
ast_cond_wait(&p->app_complete_cond, &p->lock);
|
ast_cond_wait(&p->app_complete_cond, &p->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res && p->owner)
|
if (p->owner) {
|
||||||
ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
|
ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
|
||||||
|
}
|
||||||
|
|
||||||
p->acknowledged = 0;
|
p->acknowledged = 0;
|
||||||
logintime = time(NULL) - p->loginstart;
|
|
||||||
p->loginstart = 0;
|
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
|
|
||||||
|
ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent);
|
||||||
/*** DOCUMENTATION
|
/*** DOCUMENTATION
|
||||||
<managerEventInstance>
|
<managerEventInstance>
|
||||||
<synopsis>Raised when an Agent has logged off.</synopsis>
|
<synopsis>Raised when an Agent has logged off.</synopsis>
|
||||||
@@ -2238,21 +2194,22 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
p->agent, logintime, ast_channel_uniqueid(chan));
|
p->agent, logintime, ast_channel_uniqueid(chan));
|
||||||
ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGOFF", "%s|%ld", ast_channel_name(chan), logintime);
|
ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGOFF", "%s|%ld", ast_channel_name(chan), logintime);
|
||||||
ast_verb(2, "Agent '%s' logged out\n", p->agent);
|
ast_verb(2, "Agent '%s' logged out\n", p->agent);
|
||||||
|
|
||||||
/* If there is no owner, go ahead and kill it now */
|
/* If there is no owner, go ahead and kill it now */
|
||||||
ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent);
|
|
||||||
if (p->dead && !p->owner) {
|
if (p->dead && !p->owner) {
|
||||||
agent_pvt_destroy(p);
|
agent_pvt_destroy(p);
|
||||||
}
|
}
|
||||||
}
|
AST_LIST_LOCK(&agents);
|
||||||
else {
|
} else {
|
||||||
|
/* Agent hung up before could be logged in. */
|
||||||
|
p->chan = NULL;
|
||||||
|
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
p = NULL;
|
|
||||||
}
|
}
|
||||||
res = -1;
|
res = -1;
|
||||||
} else {
|
} else {
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
errmsg = "agent-alreadyon";
|
errmsg = "agent-alreadyon";
|
||||||
p = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2261,8 +2218,7 @@ static int login_exec(struct ast_channel *chan, const char *data)
|
|||||||
ast_channel_unlock(chan);
|
ast_channel_unlock(chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!p)
|
AST_LIST_UNLOCK(&agents);
|
||||||
AST_LIST_UNLOCK(&agents);
|
|
||||||
|
|
||||||
if (!res && (max_login_tries==0 || tries < max_login_tries))
|
if (!res && (max_login_tries==0 || tries < max_login_tries))
|
||||||
res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
|
res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
|
||||||
@@ -2338,40 +2294,33 @@ static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
|
|||||||
static int agent_devicestate(const char *data)
|
static int agent_devicestate(const char *data)
|
||||||
{
|
{
|
||||||
struct agent_pvt *p;
|
struct agent_pvt *p;
|
||||||
const char *s;
|
const char *device = data;
|
||||||
ast_group_t groupmatch;
|
|
||||||
int groupoff;
|
|
||||||
int res = AST_DEVICE_INVALID;
|
int res = AST_DEVICE_INVALID;
|
||||||
|
|
||||||
s = data;
|
|
||||||
if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1))
|
|
||||||
groupmatch = (1 << groupoff);
|
|
||||||
else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
|
|
||||||
groupmatch = (1 << groupoff);
|
|
||||||
} else
|
|
||||||
groupmatch = 0;
|
|
||||||
|
|
||||||
/* Check actual logged in agents first */
|
if (device[0] == '@' || device[0] == ':') {
|
||||||
|
/* Device state of groups not supported. */
|
||||||
|
return AST_DEVICE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Want device state of a specific agent. */
|
||||||
AST_LIST_LOCK(&agents);
|
AST_LIST_LOCK(&agents);
|
||||||
AST_LIST_TRAVERSE(&agents, p, list) {
|
AST_LIST_TRAVERSE(&agents, p, list) {
|
||||||
ast_mutex_lock(&p->lock);
|
ast_mutex_lock(&p->lock);
|
||||||
if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
|
if (!p->pending && !strcmp(device, p->agent)) {
|
||||||
if (p->owner) {
|
if (p->owner) {
|
||||||
if (res != AST_DEVICE_INUSE)
|
res = AST_DEVICE_BUSY;
|
||||||
res = AST_DEVICE_BUSY;
|
} else if (p->chan) {
|
||||||
} else {
|
if (p->lastdisc.tv_sec || p->deferlogoff) {
|
||||||
if (res == AST_DEVICE_BUSY)
|
/* Agent is in wrapup time so unavailable for another call. */
|
||||||
res = AST_DEVICE_INUSE;
|
res = AST_DEVICE_INUSE;
|
||||||
if (p->chan) {
|
} else {
|
||||||
if (res == AST_DEVICE_INVALID)
|
res = AST_DEVICE_NOT_INUSE;
|
||||||
res = AST_DEVICE_UNKNOWN;
|
}
|
||||||
} else if (res == AST_DEVICE_INVALID)
|
} else {
|
||||||
res = AST_DEVICE_UNAVAILABLE;
|
res = AST_DEVICE_UNAVAILABLE;
|
||||||
}
|
|
||||||
if (!strcmp(data, p->agent)) {
|
|
||||||
ast_mutex_unlock(&p->lock);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
ast_mutex_unlock(&p->lock);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ast_mutex_unlock(&p->lock);
|
ast_mutex_unlock(&p->lock);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user