Index: channels/chan_agent.c =================================================================== --- channels/chan_agent.c (revision 379385) +++ channels/chan_agent.c (working copy) @@ -272,12 +272,11 @@ 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 */ - char logincallerid[80]; /**< Caller ID they had when they logged in */ - struct ast_channel *chan; /**< Channel we use */ - unsigned int flags; /**< Flags show if settings were applied with channel vars */ - AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */ + int app_sleep_cond; /*!< Non-zero if the login app should sleep. */ + struct ast_channel *owner; /*!< Agent */ + struct ast_channel *chan; /*!< Channel we use */ + unsigned int flags; /*!< Flags show if settings were applied with channel vars */ + AST_LIST_ENTRY(agent_pvt) list;/*!< Next Agent in the linked list. */ }; #define DATA_EXPORT_AGENT(MEMBER) \ @@ -288,8 +287,7 @@ MEMBER(agent_pvt, acknowledged, AST_DATA_BOOLEAN) \ MEMBER(agent_pvt, name, AST_DATA_STRING) \ MEMBER(agent_pvt, password, AST_DATA_PASSWORD) \ - MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER) \ - MEMBER(agent_pvt, logincallerid, AST_DATA_STRING) + MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER) AST_DATA_STRUCTURE(agent_pvt, DATA_EXPORT_AGENT); @@ -345,7 +343,6 @@ 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 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); /*! \brief Channel interface description for PBX integration */ @@ -369,7 +366,6 @@ .fixup = agent_fixup, .bridged_channel = agent_bridgedchannel, .get_base_channel = agent_get_base_channel, - .set_base_channel = agent_set_base_channel, }; /*! @@ -410,6 +406,22 @@ } /*! + * \internal + * \brief Destroy an agent pvt struct. + * + * \param doomed Agent pvt to destroy. + * + * \return Nothing + */ +static void agent_pvt_destroy(struct agent_pvt *doomed) +{ + ast_mutex_destroy(&doomed->lock); + ast_cond_destroy(&doomed->app_complete_cond); + ast_cond_destroy(&doomed->login_wait_cond); + ast_free(doomed); +} + +/*! * Adds an agent to the global list of agents. * * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" @@ -454,11 +466,16 @@ name = args.name; while (*name && *name < 33) name++; } - - /* Are we searching for the agent here ? To see if it exists already ? */ - AST_LIST_TRAVERSE(&agents, p, list) { - if (!pending && !strcmp(p->agent, agt)) - break; + + if (!pending) { + /* Are we searching for the agent here ? To see if it exists already ? */ + AST_LIST_TRAVERSE(&agents, p, list) { + if (!strcmp(p->agent, agt)) { + break; + } + } + } else { + p = NULL; } if (!p) { // Build the agent. @@ -540,18 +557,13 @@ } if (p->dead) { ast_mutex_unlock(&p->lock); - ast_mutex_destroy(&p->lock); - ast_cond_destroy(&p->app_complete_cond); - ast_cond_destroy(&p->login_wait_cond); - ast_free(p); + agent_pvt_destroy(p); } else { ast_mutex_unlock(&p->lock); } return 0; } -static int check_availability(struct agent_pvt *newlyavailable, int needlock); - static int agent_answer(struct ast_channel *ast) { ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n"); @@ -610,17 +622,10 @@ ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno; f = ast_read(p->chan); + ast->fdno = -1; } else f = &ast_null_frame; - if (!f) { - /* If there's a channel, make it NULL */ - if (p->chan) { - p->chan->_bridge = NULL; - p->chan = NULL; - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); - p->acknowledged = 0; - } - } else { + if (f) { /* 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 */ if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) { @@ -708,8 +713,7 @@ if (p->chan && !p->chan->_bridge) { if (strcasecmp(p->chan->tech->type, "Local")) { p->chan->_bridge = ast; - if (p->chan) - ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); + ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); } } ast_mutex_unlock(&p->lock); @@ -783,23 +787,19 @@ { struct agent_pvt *p = ast->tech_pvt; int res = -1; + ast_mutex_lock(&p->lock); if (p->chan && !ast_check_hangup(p->chan)) { - while (ast_channel_trylock(p->chan)) { - int res; - if ((res = ast_channel_unlock(ast))) { - ast_log(LOG_ERROR, "chan_agent bug! Channel was not locked upon entry to agent_indicate: %s\n", res > 0 ? strerror(res) : "Bad ao2obj data"); - ast_mutex_unlock(&p->lock); - return -1; - } - usleep(1); - ast_channel_lock(ast); - } + ast_channel_unlock(ast); + ast_channel_lock(p->chan); res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1; ast_channel_unlock(p->chan); - } else + ast_mutex_unlock(&p->lock); + ast_channel_lock(ast); + } else { + ast_mutex_unlock(&p->lock); res = 0; - ast_mutex_unlock(&p->lock); + } return res; } @@ -828,9 +828,8 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout) { struct agent_pvt *p = ast->tech_pvt; - int res = -1; + int res; int newstate=0; - struct ast_channel *chan; ast_mutex_lock(&p->lock); p->acknowledged = 0; @@ -842,29 +841,20 @@ return 0; } - if (!p->chan) { - ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n"); - ast_mutex_unlock(&p->lock); - return res; - } + ast_assert(p->chan != NULL); ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language); - - chan = p->chan; + ast_mutex_unlock(&p->lock); - res = ast_streamfile(chan, beep, chan->language); + res = ast_streamfile(p->chan, beep, p->chan->language); ast_debug(3, "Played beep, result '%d'\n", res); if (!res) { - res = ast_waitstream(chan, ""); + res = ast_waitstream(p->chan, ""); ast_debug(3, "Waited for stream, result '%d'\n", res); } ast_mutex_lock(&p->lock); - if (!p->chan) { - /* chan went away while we were streaming, this shouldn't be possible */ - res = -1; - } if (!res) { struct ast_format tmpfmt; @@ -872,10 +862,6 @@ ast_debug(3, "Set read format, result '%d'\n", res); if (res) 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) { @@ -895,19 +881,18 @@ agent_start_monitoring(ast, 0); p->acknowledged = 1; } - res = 0; } CLEANUP(ast, p); ast_mutex_unlock(&p->lock); if (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 */ struct ast_channel* agent_get_base_channel(struct ast_channel *chan) { - struct agent_pvt *p = NULL; + struct agent_pvt *p; struct ast_channel *base = chan; /* chan is locked by the calling function */ @@ -921,23 +906,6 @@ 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 = chan->tech_pvt; - if (!p) { - ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name); - return -1; - } - p->chan = base; - return 0; -} - static int agent_hangup(struct ast_channel *ast) { struct agent_pvt *p = ast->tech_pvt; @@ -953,13 +921,8 @@ ast_mutex_lock(&p->lock); p->owner = NULL; ast->tech_pvt = NULL; - p->app_sleep_cond = 1; 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 * later if we're called on an already downed channel * doesn't cause an agent to be logged out like when @@ -968,10 +931,7 @@ */ ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state)); - 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 */ @@ -991,70 +951,61 @@ 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'; - } else { - ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); - } - + ast_mutex_lock(&p->lock); if (p->abouttograb) { /* Let the "about to grab" thread know this isn't valid anymore, and let it kill it later */ p->abouttograb = 0; } else if (p->dead) { - ast_mutex_destroy(&p->lock); - ast_cond_destroy(&p->app_complete_cond); - ast_cond_destroy(&p->login_wait_cond); - ast_free(p); + ast_mutex_unlock(&p->lock); + agent_pvt_destroy(p); + return 0; } else { - if (p->chan) { - /* Not dead -- check availability now */ - 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); - } + /* Store last disconnect time */ + p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); } + + /* 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; } -static int agent_cont_sleep( void *data ) +static int agent_cont_sleep(void *data) { struct agent_pvt *p; int res; - p = (struct agent_pvt *)data; + p = (struct agent_pvt *) data; ast_mutex_lock(&p->lock); res = p->app_sleep_cond; - if (p->lastdisc.tv_sec) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) - res = 1; + if (res && p->lastdisc.tv_sec) { + if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { + res = 0; + } } ast_mutex_unlock(&p->lock); - if (!res) - ast_debug(5, "agent_cont_sleep() returning %d\n", res ); + if (!res) { + ast_debug(5, "agent_cont_sleep() returning %d\n", res); + } return res; } -static int agent_ack_sleep(void *data) +static int agent_ack_sleep(struct agent_pvt *p) { - struct agent_pvt *p; - int res=0; + int digit; int to = 1000; struct ast_frame *f; struct timeval start = ast_tvnow(); int ms; /* Wait a second and look for something */ - - p = (struct agent_pvt *) data; - if (!p->chan) - return -1; - while ((ms = ast_remaining_ms(start, to))) { ms = ast_waitfor(p->chan, ms); if (ms < 0) { @@ -1064,23 +1015,31 @@ return 0; } f = ast_read(p->chan); - if (!f) + if (!f) { return -1; - if (f->frametype == AST_FRAME_DTMF) - res = f->subclass.integer; - else - res = 0; + } + if (f->frametype == AST_FRAME_DTMF) { + digit = f->subclass.integer; + } else { + digit = 0; + } ast_frfree(f); ast_mutex_lock(&p->lock); if (!p->app_sleep_cond) { ast_mutex_unlock(&p->lock); return 0; - } else if (res == p->acceptdtmf) { + } + if (digit == p->acceptdtmf) { ast_mutex_unlock(&p->lock); return 1; } + if (p->lastdisc.tv_sec) { + if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { + ast_mutex_unlock(&p->lock); + return 0; + } + } ast_mutex_unlock(&p->lock); - res = 0; } return 0; } @@ -1295,10 +1254,7 @@ /* Destroy if appropriate */ if (!p->owner) { if (!p->chan) { - ast_mutex_destroy(&p->lock); - ast_cond_destroy(&p->app_complete_cond); - ast_cond_destroy(&p->login_wait_cond); - ast_free(p); + agent_pvt_destroy(p); } else { /* Cause them to hang up */ ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); @@ -1437,10 +1393,12 @@ AST_LIST_TRAVERSE(&agents, p, list) { ast_mutex_lock(&p->lock); if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->chan) + if (p->chan) { hasagent++; + } 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); /* Agent must be registered, but not have any active call, and not be in a waiting state */ if (!p->owner && p->chan) { @@ -1455,30 +1413,6 @@ } ast_mutex_unlock(&p->lock); } - if (!p) { - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->chan) { - hasagent++; - } - now = ast_tvnow(); - if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) { - p->lastdisc = ast_tv(0, 0); - /* Agent must be registered, but not have any active call, and not be in a waiting state */ - if (!p->owner && p->chan) { - /* Could still get a fixed agent */ - chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); - } - if (chan) { - ast_mutex_unlock(&p->lock); - break; - } - } - } - ast_mutex_unlock(&p->lock); - } - } if (!chan && waitforagent) { /* No agent available -- but we're requesting to wait for one. @@ -1486,10 +1420,14 @@ if (hasagent) { ast_debug(1, "Creating place holder for '%s'\n", s); p = add_agent(data, 1); - p->group = groupmatch; - chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); - if (!chan) - ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n"); + if (p) { + p->group = groupmatch; + chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); + if (!chan) { + AST_LIST_REMOVE(&agents, p, list); + agent_pvt_destroy(p); + } + } } else { ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s); } @@ -1505,10 +1443,10 @@ } if (!p->chan) { - ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n"); - *cause = AST_CAUSE_UNREGISTERED; + ast_debug(1, "Agent disconnected before we could connect the call\n"); ast_mutex_unlock(&p->lock); - agent_hangup(chan); + ast_hangup(chan); + *cause = AST_CAUSE_UNREGISTERED; return NULL; } @@ -1516,18 +1454,14 @@ * thread */ 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_debug(1, "Agent disconnected while we were connecting the call\n"); ast_mutex_unlock(&p->lock); + ast_hangup(chan); *cause = AST_CAUSE_UNREGISTERED; - agent_hangup(chan); return NULL; } @@ -1939,11 +1873,9 @@ int tries = 0; int max_login_tries = maxlogintries; struct agent_pvt *p; - struct ast_module_user *u; - char user[AST_MAX_AGENT] = ""; + char user[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 *parse; AST_DECLARE_APP_ARGS(args, @@ -1955,9 +1887,9 @@ int play_announcement = 1; char agent_goodbye[AST_MAX_FILENAME_LEN]; int update_cdr = updatecdr; - char *filename = "agent-loginok"; - u = ast_module_user_add(chan); + user[0] = '\0'; + xpass[0] = '\0'; parse = ast_strdupa(data); @@ -2029,15 +1961,12 @@ AST_LIST_LOCK(&agents); AST_LIST_TRAVERSE(&agents, p, list) { int unlock_channel = 1; + ast_channel_lock(chan); ast_mutex_lock(&p->lock); if (!strcmp(p->agent, user) && !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 */ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { @@ -2086,21 +2015,13 @@ ast_channel_unlock(chan); unlock_channel = 0; /* End Channel Specific Agent Overrides */ + if (!p->chan) { - long logintime; - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); + /* Ensure nobody else can be this agent until we're done. */ + p->chan = chan; - p->logincallerid[0] = '\0'; p->acknowledged = 0; - - ast_mutex_unlock(&p->lock); - AST_LIST_UNLOCK(&agents); - if( !res && play_announcement==1 ) - res = ast_streamfile(chan, filename, chan->language); - if (!res) - ast_waitstream(chan, ""); - AST_LIST_LOCK(&agents); - ast_mutex_lock(&p->lock); + if (!res) { struct ast_format tmpfmt; res = ast_set_read_format_from_cap(chan, chan->nativeformats); @@ -2115,56 +2036,63 @@ ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(&tmpfmt)); } } - /* Check once more just in case */ - if (p->chan) - res = -1; + if (!res && play_announcement == 1) { + ast_mutex_unlock(&p->lock); + AST_LIST_UNLOCK(&agents); + res = ast_streamfile(chan, "agent-loginok", chan->language); + if (!res) { + ast_waitstream(chan, ""); + } + AST_LIST_LOCK(&agents); + ast_mutex_lock(&p->lock); + } + 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, S_OR(p->moh, NULL), !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); + manager_event(EVENT_FLAG_AGENT, "Agentlogin", "Agent: %s\r\n" "Channel: %s\r\n" "Uniqueid: %s\r\n", p->agent, chan->name, chan->uniqueid); if (update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); + snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "%s", agent); ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name); ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent, ast_getformatname(&chan->readformat), ast_getformatname(&chan->writeformat)); - /* 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_LIST_UNLOCK(&agents); - ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Agent/%s", p->agent); + while (res >= 0) { ast_mutex_lock(&p->lock); - if (p->deferlogoff && p->chan) { - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); + if (p->deferlogoff) { p->deferlogoff = 0; + ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT); + ast_mutex_unlock(&p->lock); + break; } - if (p->chan != chan) - res = -1; ast_mutex_unlock(&p->lock); - /* Yield here so other interested threads can kick in. */ - sched_yield(); - if (res) - break; AST_LIST_LOCK(&agents); ast_mutex_lock(&p->lock); if (p->lastdisc.tv_sec) { 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); - 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) { check_beep(p, 0); } else { @@ -2175,47 +2103,53 @@ ast_mutex_unlock(&p->lock); 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); - if (p->app_lock_flag == 1) { + if (p->app_lock_flag) { ast_cond_signal(&p->login_wait_cond); 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); + if (p->ackcall) { 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 { 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); - /* Log us off if appropriate */ - if (p->chan == chan) { - p->chan = NULL; - } + /* Logoff this channel */ + p->chan = NULL; + logintime = time(NULL) - p->loginstart; + p->loginstart = 0; + /* 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_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"); + } p->acknowledged = 0; - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; ast_mutex_unlock(&p->lock); + + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent); manager_event(EVENT_FLAG_AGENT, "Agentlogoff", "Agent: %s\r\n" "Logintime: %ld\r\n" @@ -2223,24 +2157,22 @@ p->agent, logintime, chan->uniqueid); ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime); ast_verb(2, "Agent '%s' logged out\n", p->agent); + /* 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) { - ast_mutex_destroy(&p->lock); - ast_cond_destroy(&p->app_complete_cond); - ast_cond_destroy(&p->login_wait_cond); - ast_free(p); + agent_pvt_destroy(p); } - } - else { + AST_LIST_LOCK(&agents); + } else { + /* Agent hung up before could be logged in. */ + p->chan = NULL; + ast_mutex_unlock(&p->lock); - p = NULL; } res = -1; } else { ast_mutex_unlock(&p->lock); errmsg = "agent-alreadyon"; - p = NULL; } break; } @@ -2249,8 +2181,7 @@ ast_channel_unlock(chan); } } - if (!p) - AST_LIST_UNLOCK(&agents); + AST_LIST_UNLOCK(&agents); if (!res && (max_login_tries==0 || tries < max_login_tries)) res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); @@ -2259,8 +2190,6 @@ if (!res) res = ast_safe_sleep(chan, 500); - ast_module_user_remove(u); - return -1; } @@ -2328,40 +2257,33 @@ static int agent_devicestate(void *data) { struct agent_pvt *p; - char *s; - ast_group_t groupmatch; - int groupoff; + const char *device = data; 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_TRAVERSE(&agents, p, list) { 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 (res != AST_DEVICE_INUSE) - res = AST_DEVICE_BUSY; - } else { - if (res == AST_DEVICE_BUSY) + res = AST_DEVICE_BUSY; + } else if (p->chan) { + if (p->lastdisc.tv_sec || p->deferlogoff) { + /* Agent is in wrapup time so unavailable for another call. */ res = AST_DEVICE_INUSE; - if (p->chan) { - if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNKNOWN; - } else if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNAVAILABLE; + } else { + res = AST_DEVICE_NOT_INUSE; + } + } else { + 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); }