📄 chan_agent.c
字号:
}static int agent_hangup(struct ast_channel *ast){ struct agent_pvt *p = ast->tech_pvt; int howlong = 0; const char *status; ast_mutex_lock(&p->lock); p->owner = NULL; ast->tech_pvt = NULL; p->app_sleep_cond = 1; p->acknowledged = 0; /* 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 * agent_request() is followed immediately by agent_hangup() * as in apps/app_chanisavail.c:chanavail_exec() */ if (option_debug) ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state)); if (p->start && (ast->_state != AST_STATE_UP)) { howlong = time(NULL) - p->start; p->start = 0; } else if (ast->_state == AST_STATE_RESERVED) howlong = 0; else p->start = 0; if (p->chan) { p->chan->_bridge = NULL; /* If they're dead, go ahead and hang up on the agent now */ if (!ast_strlen_zero(p->loginchan)) { /* Store last disconnect time */ if (p->wrapuptime) p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); else p->lastdisc = ast_tv(0,0); if (p->chan) { status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { long logintime = time(NULL) - p->loginstart; p->loginstart = 0; ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name); agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); } /* Recognize the hangup and pass it along immediately */ ast_hangup(p->chan); p->chan = NULL; p->inherited_devicestate = -1; ast_device_state_changed("Agent/%s", p->agent); } ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) { long logintime = time(NULL) - p->loginstart; p->loginstart = 0; if (!p->deferlogoff) ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); p->deferlogoff = 0; agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff"); if (persistent_agents) dump_agents(); } } else if (p->dead) { ast_channel_lock(p->chan); 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); } } ast_mutex_unlock(&p->lock); /* Only register a device state change if the agent is still logged in */ if (!p->loginstart) { p->loginchan[0] = '\0'; p->logincallerid[0] = '\0'; if (persistent_agents) dump_agents(); } else { ast_device_state_changed("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 */ p->abouttograb = 0; } else if (p->dead) { ast_mutex_destroy(&p->lock); ast_mutex_destroy(&p->app_lock); free(p); } 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); } /* Release ownership of the agent to other threads (presumably running the login app). */ if (ast_strlen_zero(p->loginchan)) ast_mutex_unlock(&p->app_lock); } return 0;}static int agent_cont_sleep( void *data ){ struct agent_pvt *p; int res; 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; } ast_mutex_unlock(&p->lock); if(option_debug > 4 && !res ) ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res ); return res;}static int agent_ack_sleep(void *data){ struct agent_pvt *p; int res=0; int to = 1000; struct ast_frame *f; /* Wait a second and look for something */ p = (struct agent_pvt *) data; if (!p->chan) return -1; for(;;) { to = ast_waitfor(p->chan, to); if (to < 0) return -1; if (!to) return 0; f = ast_read(p->chan); if (!f) return -1; if (f->frametype == AST_FRAME_DTMF) res = f->subclass; else res = 0; ast_frfree(f); ast_mutex_lock(&p->lock); if (!p->app_sleep_cond) { ast_mutex_unlock(&p->lock); return 0; } else if (res == '#') { ast_mutex_unlock(&p->lock); return 1; } ast_mutex_unlock(&p->lock); res = 0; } return res;}static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge){ struct agent_pvt *p = bridge->tech_pvt; struct ast_channel *ret = NULL; if (p) { if (chan == p->chan) ret = bridge->_bridge; else if (chan == bridge->_bridge) ret = p->chan; } if (option_debug) ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>"); return ret;}/*! \brief Create new agent channel */static struct ast_channel *agent_new(struct agent_pvt *p, int state){ struct ast_channel *tmp;#if 0 if (!p->chan) { ast_log(LOG_WARNING, "No channel? :(\n"); return NULL; }#endif if (p->pending) tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, ast_random() & 0xffff); else tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); return NULL; } tmp->tech = &agent_tech; if (p->chan) { tmp->nativeformats = p->chan->nativeformats; tmp->writeformat = p->chan->writeformat; tmp->rawwriteformat = p->chan->writeformat; tmp->readformat = p->chan->readformat; tmp->rawreadformat = p->chan->readformat; ast_string_field_set(tmp, language, p->chan->language); ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); /* XXX Is this really all we copy form the originating channel?? */ } else { tmp->nativeformats = AST_FORMAT_SLINEAR; tmp->writeformat = AST_FORMAT_SLINEAR; tmp->rawwriteformat = AST_FORMAT_SLINEAR; tmp->readformat = AST_FORMAT_SLINEAR; tmp->rawreadformat = AST_FORMAT_SLINEAR; } /* Safe, agentlock already held */ tmp->tech_pvt = p; p->owner = tmp; /* XXX: this needs fixing */#if 0 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);#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; if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) { if (p->chan) { ast_queue_frame(p->chan, &ast_null_frame); ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ ast_mutex_lock(&p->app_lock); 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. */ ast_mutex_unlock(&p->app_lock); 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); p->owning_app = pthread_self(); /* After the above step, there should not be any blockers. */ if (p->chan) { if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) { ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" ); ast_assert(ast_test_flag(p->chan, AST_FLAG_BLOCKING) == 0); } } return tmp;}/*! * Read configuration data. The file named agents.conf. * * \returns Always 0, or so it seems. */static int read_agent_config(void){ struct ast_config *cfg; struct ast_config *ucfg; struct ast_variable *v; struct agent_pvt *p; const char *general_val; const char *catname; const char *hasagent; int genhasagent; group = 0; autologoff = 0; wrapuptime = 0; ackcall = 0; endcall = 1; cfg = ast_config_load(config); if (!cfg) { ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n"); return 0; } AST_LIST_LOCK(&agents); AST_LIST_TRAVERSE(&agents, p, list) { p->dead = 1; } strcpy(moh, "default"); /* set the default recording values */ recordagentcalls = 0; strcpy(recordformat, "wav"); strcpy(recordformatext, "wav"); urlprefix[0] = '\0'; savecallsin[0] = '\0'; /* Read in [general] section for persistence */ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents"))) persistent_agents = ast_true(general_val); multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin")); /* Read in the [agents] section */ v = ast_variable_browse(cfg, "agents"); while(v) { /* Create the interface list */ if (!strcasecmp(v->name, "agent")) { add_agent(v->value, 0); } else if (!strcasecmp(v->name, "group")) { group = ast_get_group(v->value); } else if (!strcasecmp(v->name, "autologoff")) { autologoff = atoi(v->value); if (autologoff < 0) autologoff = 0; } else if (!strcasecmp(v->name, "ackcall")) { if (!strcasecmp(v->value, "always")) ackcall = 2; else if (ast_true(v->value)) ackcall = 1; else ackcall = 0; } else if (!strcasecmp(v->name, "endcall")) { endcall = ast_true(v->value); } else if (!strcasecmp(v->name, "wrapuptime")) { wrapuptime = atoi(v->value); if (wrapuptime < 0) wrapuptime = 0; } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) { maxlogintries = atoi(v->value); if (maxlogintries < 0) maxlogintries = 0; } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) { strcpy(agentgoodbye,v->value); } else if (!strcasecmp(v->name, "musiconhold")) { ast_copy_string(moh, v->value, sizeof(moh)); } else if (!strcasecmp(v->name, "updatecdr")) { if (ast_true(v->value)) updatecdr = 1; else updatecdr = 0; } else if (!strcasecmp(v->name, "autologoffunavail")) { if (ast_true(v->value)) autologoffunavail = 1; else autologoffunavail = 0; } else if (!strcasecmp(v->name, "recordagentcalls")) { recordagentcalls = ast_true(v->value); } else if (!strcasecmp(v->name, "recordformat")) { ast_copy_string(recordformat, v->value, sizeof(recordformat)); if (!strcasecmp(v->value, "wav49")) strcpy(recordformatext, "WAV"); else ast_copy_string(recordformatext, v->value, sizeof(recordformatext)); } else if (!strcasecmp(v->name, "urlprefix")) { ast_copy_string(urlprefix, v->value, sizeof(urlprefix)); if (urlprefix[strlen(urlprefix) - 1] != '/') strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1); } else if (!strcasecmp(v->name, "savecallsin")) { if (v->value[0] == '/') ast_copy_string(savecallsin, v->value, sizeof(savecallsin)); else snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value); if (savecallsin[strlen(savecallsin) - 1] != '/') strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1); } else if (!strcasecmp(v->name, "custom_beep")) { ast_copy_string(beep, v->value, sizeof(beep)); } v = v->next; } if ((ucfg = ast_config_load("users.conf"))) { genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent")); catname = ast_category_browse(ucfg, NULL); while(catname) { if (strcasecmp(catname, "general")) { hasagent = ast_variable_retrieve(ucfg, catname, "hasagent"); if (ast_true(hasagent) || (!hasagent && genhasagent)) { char tmp[256]; const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname"); const char *secret = ast_variable_retrieve(ucfg, catname, "secret"); if (!fullname) fullname = "";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -