Implementation of Agents (proxy channel). More...
#include "asterisk.h"
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/acl.h"
#include "asterisk/callerid.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/manager.h"
#include "asterisk/features.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
#include "asterisk/data.h"
Go to the source code of this file.
Data Structures | |
struct | agent_pvt |
Structure representing an agent. More... | |
Defines | |
#define | AST_MAX_AGENT 80 |
#define | AST_MAX_BUF 256 |
#define | AST_MAX_FILENAME_LEN 256 |
#define | CHECK_FORMATS(ast, p) |
#define | CLEANUP(ast, p) |
Cleanup moves all the relevant FD's from the 2nd to the first, but retains things properly for a timingfd XXX This might need more work if agents were logged in as agents or other totally impractical combinations XXX. | |
#define | DATA_EXPORT_AGENT(MEMBER) |
#define | DEFAULT_ACCEPTDTMF '#' |
#define | DEFAULT_ENDDTMF '*' |
#define | GETAGENTBYCALLERID "AGENTBYCALLERID" |
#define | PA_MAX_LEN 2048 |
Enumerations | |
enum | { AGENT_FLAG_ACKCALL = (1 << 0), AGENT_FLAG_AUTOLOGOFF = (1 << 1), AGENT_FLAG_WRAPUPTIME = (1 << 2), AGENT_FLAG_ACCEPTDTMF = (1 << 3), AGENT_FLAG_ENDDTMF = (1 << 4) } |
Functions | |
static int | __agent_start_monitoring (struct ast_channel *ast, struct agent_pvt *p, int needlock) |
static int | action_agent_logoff (struct mansession *s, const struct message *m) |
static int | action_agents (struct mansession *s, const struct message *m) |
static struct agent_pvt * | add_agent (const char *agent, int pending) |
static int | agent_ack_sleep (struct agent_pvt *p) |
static int | agent_answer (struct ast_channel *ast) |
static struct ast_channel * | agent_bridgedchannel (struct ast_channel *chan, struct ast_channel *bridge) |
static int | agent_call (struct ast_channel *ast, char *dest, int timeout) |
static int | agent_cleanup (struct agent_pvt *p) |
static int | agent_cont_sleep (void *data) |
static int | agent_devicestate (void *data) |
Part of PBX channel interface. | |
static int | agent_digit_begin (struct ast_channel *ast, char digit) |
static int | agent_digit_end (struct ast_channel *ast, char digit, unsigned int duration) |
static int | agent_fixup (struct ast_channel *oldchan, struct ast_channel *newchan) |
static struct ast_channel * | agent_get_base_channel (struct ast_channel *chan) |
return the channel or base channel if one exists. This function assumes the channel it is called on is already locked | |
static int | agent_hangup (struct ast_channel *ast) |
static int | agent_indicate (struct ast_channel *ast, int condition, const void *data, size_t datalen) |
static struct ast_channel * | agent_lock_owner (struct agent_pvt *pvt) |
Locks the owning channel for a LOCKED pvt while obeying locking order. The pvt must enter this function locked and will be returned locked, but this function will unlock the pvt for a short time, so it can't be used while expecting the pvt to remain static. If function returns a non NULL channel, it will need to be unlocked and unrefed once it is no longer needed. | |
static int | agent_logoff (const char *agent, int soft) |
static char * | agent_logoff_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static struct ast_channel * | agent_new (struct agent_pvt *p, int state, const char *linkedid) |
Create new agent channel. | |
static void | agent_pvt_destroy (struct agent_pvt *doomed) |
static struct ast_frame * | agent_read (struct ast_channel *ast) |
static struct ast_channel * | agent_request (const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) |
Part of the Asterisk PBX interface. | |
static int | agent_sendhtml (struct ast_channel *ast, int subclass, const char *data, int datalen) |
static int | agent_sendtext (struct ast_channel *ast, const char *text) |
static int | agent_start_monitoring (struct ast_channel *ast, int needlock) |
static int | agent_write (struct ast_channel *ast, struct ast_frame *f) |
static int | agentmonitoroutgoing_exec (struct ast_channel *chan, const char *data) |
Called by the AgentMonitorOutgoing application (from the dial plan). | |
static int | agents_data_provider_get (const struct ast_data_search *search, struct ast_data *data_root) |
static char * | agents_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | agents_show_online (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
AST_DATA_STRUCTURE (agent_pvt, DATA_EXPORT_AGENT) | |
static | AST_LIST_HEAD_STATIC (agents, agent_pvt) |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"Agent Proxy Channel",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DRIVER,.nonoptreq="res_monitor,chan_local",) | |
static int | check_availability (struct agent_pvt *newlyavailable, int needlock) |
static int | check_beep (struct agent_pvt *newlyavailable, int needlock) |
static char * | complete_agent_logoff_cmd (const char *line, const char *word, int pos, int state) |
static struct agent_pvt * | find_agent (char *agentid) |
static int | function_agent (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
static int | load_module (void) |
Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other things it registers applications, cli commands and reads the cofiguration file. | |
static int | login_exec (struct ast_channel *chan, const char *data) |
Log in agent application. | |
static force_inline int | powerof (unsigned int d) |
static int | read_agent_config (int reload) |
static int | reload (void) |
static int | unload_module (void) |
Variables | |
static char | acceptdtmf = DEFAULT_ACCEPTDTMF |
static int | ackcall |
static struct ast_custom_function | agent_function |
static const char | agent_logoff_usage [] |
static struct ast_channel_tech | agent_tech |
Channel interface description for PBX integration. | |
static char | agentgoodbye [AST_MAX_FILENAME_LEN] = "vm-goodbye" |
static struct ast_data_handler | agents_data_provider |
static struct ast_data_entry | agents_data_providers [] |
static const char | app [] = "AgentLogin" |
static const char | app3 [] = "AgentMonitorOutgoing" |
static int | autologoff |
static int | autologoffunavail = 0 |
static char | beep [AST_MAX_BUF] = "beep" |
static struct ast_cli_entry | cli_agents [] |
static const char | config [] = "agents.conf" |
static int | endcall |
static char | enddtmf = DEFAULT_ENDDTMF |
static ast_group_t | group |
static int | maxlogintries = 3 |
static char | moh [80] = "default" |
static const char | pa_family [] = "Agents" |
static int | recordagentcalls = 0 |
static char | recordformat [AST_MAX_BUF] = "" |
static char | recordformatext [AST_MAX_BUF] = "" |
static char | savecallsin [AST_MAX_BUF] = "" |
static const char | tdesc [] = "Call Agent Proxy Channel" |
static int | updatecdr = 0 |
static char | urlprefix [AST_MAX_BUF] = "" |
static int | wrapuptime |
Implementation of Agents (proxy channel).
This file is the implementation of Agents modules. It is a dynamic module that is loaded by Asterisk.
Definition in file chan_agent.c.
#define AST_MAX_AGENT 80 |
Agent ID or Password max length
Definition at line 211 of file chan_agent.c.
Referenced by agentmonitoroutgoing_exec(), complete_agent_logoff_cmd(), and login_exec().
#define AST_MAX_BUF 256 |
Definition at line 212 of file chan_agent.c.
Referenced by __agent_start_monitoring(), agentmonitoroutgoing_exec(), agents_show(), and agents_show_online().
#define AST_MAX_FILENAME_LEN 256 |
Definition at line 213 of file chan_agent.c.
Referenced by login_exec().
#define CHECK_FORMATS | ( | ast, | |||
p | ) |
Definition at line 296 of file chan_agent.c.
Referenced by agent_read(), and agent_write().
#define CLEANUP | ( | ast, | |||
p | ) |
Cleanup moves all the relevant FD's from the 2nd to the first, but retains things properly for a timingfd XXX This might need more work if agents were logged in as agents or other totally impractical combinations XXX.
Definition at line 318 of file chan_agent.c.
Referenced by agent_call(), agent_read(), and agent_write().
#define DATA_EXPORT_AGENT | ( | MEMBER | ) |
Definition at line 282 of file chan_agent.c.
#define DEFAULT_ACCEPTDTMF '#' |
Definition at line 218 of file chan_agent.c.
#define DEFAULT_ENDDTMF '*' |
Definition at line 219 of file chan_agent.c.
#define GETAGENTBYCALLERID "AGENTBYCALLERID" |
Definition at line 241 of file chan_agent.c.
Referenced by agentmonitoroutgoing_exec().
#define PA_MAX_LEN 2048 |
The maximum length of each persistent member agent database entry
Definition at line 216 of file chan_agent.c.
anonymous enum |
AGENT_FLAG_ACKCALL | |
AGENT_FLAG_AUTOLOGOFF | |
AGENT_FLAG_WRAPUPTIME | |
AGENT_FLAG_ACCEPTDTMF | |
AGENT_FLAG_ENDDTMF |
Definition at line 243 of file chan_agent.c.
00243 { 00244 AGENT_FLAG_ACKCALL = (1 << 0), 00245 AGENT_FLAG_AUTOLOGOFF = (1 << 1), 00246 AGENT_FLAG_WRAPUPTIME = (1 << 2), 00247 AGENT_FLAG_ACCEPTDTMF = (1 << 3), 00248 AGENT_FLAG_ENDDTMF = (1 << 4), 00249 };
static int __agent_start_monitoring | ( | struct ast_channel * | ast, | |
struct agent_pvt * | p, | |||
int | needlock | |||
) | [static] |
Definition at line 574 of file chan_agent.c.
References agent_pvt::agent, ast_cdr_alloc(), ast_cdr_setuserfield(), ast_log(), AST_MAX_BUF, ast_monitor_setjoinfiles(), ast_monitor_start(), ast_verbose, ast_channel::cdr, LOG_ERROR, ast_channel::monitor, X_REC_IN, and X_REC_OUT.
Referenced by agent_start_monitoring(), and agentmonitoroutgoing_exec().
00575 { 00576 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer; 00577 char filename[AST_MAX_BUF]; 00578 int res = -1; 00579 if (!p) 00580 return -1; 00581 if (!ast->monitor) { 00582 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid); 00583 /* substitute . for - */ 00584 if ((pointer = strchr(filename, '.'))) 00585 *pointer = '-'; 00586 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename); 00587 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT); 00588 ast_monitor_setjoinfiles(ast, 1); 00589 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext); 00590 #if 0 00591 ast_verbose("name is %s, link is %s\n",tmp, tmp2); 00592 #endif 00593 if (!ast->cdr) 00594 ast->cdr = ast_cdr_alloc(); 00595 ast_cdr_setuserfield(ast, tmp2); 00596 res = 0; 00597 } else 00598 ast_log(LOG_ERROR, "Recording already started on that call.\n"); 00599 return res; 00600 }
static int action_agent_logoff | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Sets an agent as no longer logged in in the Manager API. It is registered on load_module() and it gets called by the manager backend.
s | ||
m |
Definition at line 1653 of file chan_agent.c.
References agent_pvt::agent, agent_logoff(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), and astman_send_error().
Referenced by load_module().
01654 { 01655 const char *agent = astman_get_header(m, "Agent"); 01656 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */ 01657 int soft; 01658 int ret; /* return value of agent_logoff */ 01659 01660 if (ast_strlen_zero(agent)) { 01661 astman_send_error(s, m, "No agent specified"); 01662 return 0; 01663 } 01664 01665 soft = ast_true(soft_s) ? 1 : 0; 01666 ret = agent_logoff(agent, soft); 01667 if (ret == 0) 01668 astman_send_ack(s, m, "Agent logged out"); 01669 else 01670 astman_send_error(s, m, "No such agent"); 01671 01672 return 0; 01673 }
static int action_agents | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Lists agents and their status to the Manager API. It is registered on load_module() and it gets called by the manager backend. This function locks both the pvt and the channel that owns it for a while, but does not keep these locks.
s | ||
m |
Definition at line 1494 of file chan_agent.c.
References ast_channel::_bridge, agent_pvt::agent, agent_lock_owner(), ast_bridged_channel(), ast_channel_unlock, ast_channel_unref, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_strdupa, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), ast_channel::bridge, ast_channel::caller, agent_pvt::chan, ast_party_caller::id, agent_pvt::lock, agent_pvt::loginstart, agent_pvt::name, ast_party_id::number, agent_pvt::owner, S_COR, S_OR, status, ast_party_number::str, and ast_party_number::valid.
Referenced by load_module().
01495 { 01496 const char *id = astman_get_header(m,"ActionID"); 01497 char idText[256] = ""; 01498 struct agent_pvt *p; 01499 char *username = NULL; 01500 char *loginChan = NULL; 01501 char *talkingto = NULL; 01502 char *talkingtoChan = NULL; 01503 char *status = NULL; 01504 struct ast_channel *bridge; 01505 01506 if (!ast_strlen_zero(id)) 01507 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id); 01508 astman_send_ack(s, m, "Agents will follow"); 01509 AST_LIST_LOCK(&agents); 01510 AST_LIST_TRAVERSE(&agents, p, list) { 01511 struct ast_channel *owner; 01512 ast_mutex_lock(&p->lock); 01513 owner = agent_lock_owner(p); 01514 01515 /* Status Values: 01516 AGENT_LOGGEDOFF - Agent isn't logged in 01517 AGENT_IDLE - Agent is logged in, and waiting for call 01518 AGENT_ONCALL - Agent is logged in, and on a call 01519 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */ 01520 01521 username = S_OR(p->name, "None"); 01522 01523 /* Set a default status. It 'should' get changed. */ 01524 status = "AGENT_UNKNOWN"; 01525 01526 if (p->chan) { 01527 loginChan = ast_strdupa(p->chan->name); 01528 if (owner && owner->_bridge) { 01529 talkingto = S_COR(p->chan->caller.id.number.valid, 01530 p->chan->caller.id.number.str, "n/a"); 01531 if ((bridge = ast_bridged_channel(owner))) { 01532 talkingtoChan = ast_strdupa(bridge->name); 01533 } else { 01534 talkingtoChan = "n/a"; 01535 } 01536 status = "AGENT_ONCALL"; 01537 } else { 01538 talkingto = "n/a"; 01539 talkingtoChan = "n/a"; 01540 status = "AGENT_IDLE"; 01541 } 01542 } else { 01543 loginChan = "n/a"; 01544 talkingto = "n/a"; 01545 talkingtoChan = "n/a"; 01546 status = "AGENT_LOGGEDOFF"; 01547 } 01548 01549 if (owner) { 01550 ast_channel_unlock(owner); 01551 owner = ast_channel_unref(owner); 01552 } 01553 01554 astman_append(s, "Event: Agents\r\n" 01555 "Agent: %s\r\n" 01556 "Name: %s\r\n" 01557 "Status: %s\r\n" 01558 "LoggedInChan: %s\r\n" 01559 "LoggedInTime: %d\r\n" 01560 "TalkingTo: %s\r\n" 01561 "TalkingToChan: %s\r\n" 01562 "%s" 01563 "\r\n", 01564 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText); 01565 ast_mutex_unlock(&p->lock); 01566 } 01567 AST_LIST_UNLOCK(&agents); 01568 astman_append(s, "Event: AgentsComplete\r\n" 01569 "%s" 01570 "\r\n",idText); 01571 return 0; 01572 }
static struct agent_pvt* add_agent | ( | const char * | agent, | |
int | pending | |||
) | [static, read] |
Adds an agent to the global list of agents.
agent | A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" | |
pending | If it is pending or not. |
Definition at line 433 of file chan_agent.c.
References agent_pvt::acceptdtmf, agent_pvt::ackcall, agent_pvt::agent, AGENT_FLAG_ACCEPTDTMF, AGENT_FLAG_ACKCALL, AGENT_FLAG_AUTOLOGOFF, AGENT_FLAG_ENDDTMF, AGENT_FLAG_WRAPUPTIME, agent_pvt::app_complete_cond, agent_pvt::app_lock_flag, agent_pvt::app_sleep_cond, args, AST_APP_ARG, ast_calloc, ast_cond_init, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_init, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_tvnow(), agent_pvt::autologoff, agent_pvt::dead, agent_pvt::enddtmf, agent_pvt::group, agent_pvt::lastdisc, agent_pvt::lock, LOG_WARNING, agent_pvt::login_wait_cond, agent_pvt::moh, agent_pvt::name, parse(), agent_pvt::password, agent_pvt::pending, and agent_pvt::wrapuptime.
Referenced by agent_request(), and read_agent_config().
00434 { 00435 char *parse; 00436 AST_DECLARE_APP_ARGS(args, 00437 AST_APP_ARG(agt); 00438 AST_APP_ARG(password); 00439 AST_APP_ARG(name); 00440 ); 00441 char *password = NULL; 00442 char *name = NULL; 00443 char *agt = NULL; 00444 struct agent_pvt *p; 00445 00446 parse = ast_strdupa(agent); 00447 00448 /* Extract username (agt), password and name from agent (args). */ 00449 AST_STANDARD_APP_ARGS(args, parse); 00450 00451 if(args.argc == 0) { 00452 ast_log(LOG_WARNING, "A blank agent line!\n"); 00453 return NULL; 00454 } 00455 00456 if(ast_strlen_zero(args.agt) ) { 00457 ast_log(LOG_WARNING, "An agent line with no agentid!\n"); 00458 return NULL; 00459 } else 00460 agt = args.agt; 00461 00462 if(!ast_strlen_zero(args.password)) { 00463 password = args.password; 00464 while (*password && *password < 33) password++; 00465 } 00466 if(!ast_strlen_zero(args.name)) { 00467 name = args.name; 00468 while (*name && *name < 33) name++; 00469 } 00470 00471 if (!pending) { 00472 /* Are we searching for the agent here ? To see if it exists already ? */ 00473 AST_LIST_TRAVERSE(&agents, p, list) { 00474 if (!strcmp(p->agent, agt)) { 00475 break; 00476 } 00477 } 00478 } else { 00479 p = NULL; 00480 } 00481 if (!p) { 00482 // Build the agent. 00483 if (!(p = ast_calloc(1, sizeof(*p)))) 00484 return NULL; 00485 ast_copy_string(p->agent, agt, sizeof(p->agent)); 00486 ast_mutex_init(&p->lock); 00487 ast_cond_init(&p->app_complete_cond, NULL); 00488 ast_cond_init(&p->login_wait_cond, NULL); 00489 p->app_lock_flag = 0; 00490 p->app_sleep_cond = 1; 00491 p->group = group; 00492 p->pending = pending; 00493 AST_LIST_INSERT_TAIL(&agents, p, list); 00494 } 00495 00496 ast_copy_string(p->password, password ? password : "", sizeof(p->password)); 00497 ast_copy_string(p->name, name ? name : "", sizeof(p->name)); 00498 ast_copy_string(p->moh, moh, sizeof(p->moh)); 00499 if (!ast_test_flag(p, AGENT_FLAG_ACKCALL)) { 00500 p->ackcall = ackcall; 00501 } 00502 if (!ast_test_flag(p, AGENT_FLAG_AUTOLOGOFF)) { 00503 p->autologoff = autologoff; 00504 } 00505 if (!ast_test_flag(p, AGENT_FLAG_ACCEPTDTMF)) { 00506 p->acceptdtmf = acceptdtmf; 00507 } 00508 if (!ast_test_flag(p, AGENT_FLAG_ENDDTMF)) { 00509 p->enddtmf = enddtmf; 00510 } 00511 00512 /* If someone reduces the wrapuptime and reloads, we want it 00513 * to change the wrapuptime immediately on all calls */ 00514 if (!ast_test_flag(p, AGENT_FLAG_WRAPUPTIME) && p->wrapuptime > wrapuptime) { 00515 struct timeval now = ast_tvnow(); 00516 /* XXX check what is this exactly */ 00517 00518 /* We won't be pedantic and check the tv_usec val */ 00519 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) { 00520 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000; 00521 p->lastdisc.tv_usec = now.tv_usec; 00522 } 00523 } 00524 p->wrapuptime = wrapuptime; 00525 00526 if (pending) 00527 p->dead = 1; 00528 else 00529 p->dead = 0; 00530 return p; 00531 }
static int agent_ack_sleep | ( | struct agent_pvt * | p | ) | [static] |
Definition at line 999 of file chan_agent.c.
References agent_pvt::acceptdtmf, agent_pvt::app_sleep_cond, AST_FRAME_DTMF, ast_frfree, ast_mutex_lock, ast_mutex_unlock, ast_read(), ast_remaining_ms(), ast_tvdiff_ms(), ast_tvnow(), ast_waitfor(), agent_pvt::chan, f, ast_frame::frametype, ast_frame_subclass::integer, agent_pvt::lastdisc, agent_pvt::lock, and ast_frame::subclass.
Referenced by login_exec().
01000 { 01001 int digit; 01002 int to = 1000; 01003 struct ast_frame *f; 01004 struct timeval start = ast_tvnow(); 01005 int ms; 01006 01007 /* Wait a second and look for something */ 01008 while ((ms = ast_remaining_ms(start, to))) { 01009 ms = ast_waitfor(p->chan, ms); 01010 if (ms < 0) { 01011 return -1; 01012 } 01013 if (ms == 0) { 01014 return 0; 01015 } 01016 f = ast_read(p->chan); 01017 if (!f) { 01018 return -1; 01019 } 01020 if (f->frametype == AST_FRAME_DTMF) { 01021 digit = f->subclass.integer; 01022 } else { 01023 digit = 0; 01024 } 01025 ast_frfree(f); 01026 ast_mutex_lock(&p->lock); 01027 if (!p->app_sleep_cond) { 01028 ast_mutex_unlock(&p->lock); 01029 return 0; 01030 } 01031 if (digit == p->acceptdtmf) { 01032 ast_mutex_unlock(&p->lock); 01033 return 1; 01034 } 01035 if (p->lastdisc.tv_sec) { 01036 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { 01037 ast_mutex_unlock(&p->lock); 01038 return 0; 01039 } 01040 } 01041 ast_mutex_unlock(&p->lock); 01042 } 01043 return 0; 01044 }
static int agent_answer | ( | struct ast_channel * | ast | ) | [static] |
Definition at line 568 of file chan_agent.c.
References ast_log(), and LOG_WARNING.
00569 { 00570 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n"); 00571 return -1; 00572 }
static struct ast_channel * agent_bridgedchannel | ( | struct ast_channel * | chan, | |
struct ast_channel * | bridge | |||
) | [static, read] |
Definition at line 1046 of file chan_agent.c.
References ast_channel::_bridge, ast_debug, agent_pvt::chan, and ast_channel::tech_pvt.
01047 { 01048 struct agent_pvt *p = bridge->tech_pvt; 01049 struct ast_channel *ret = NULL; 01050 01051 if (p) { 01052 if (chan == p->chan) 01053 ret = bridge->_bridge; 01054 else if (chan == bridge->_bridge) 01055 ret = p->chan; 01056 } 01057 01058 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>"); 01059 return ret; 01060 }
static int agent_call | ( | struct ast_channel * | ast, | |
char * | dest, | |||
int | timeout | |||
) | [static] |
Definition at line 829 of file chan_agent.c.
References agent_pvt::ackcall, agent_pvt::acknowledged, agent_pvt::agent, agent_start_monitoring(), ast_assert, ast_best_codec(), ast_debug, ast_getformatname(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_set_read_format(), ast_set_write_format(), ast_setstate(), AST_STATE_DIALING, AST_STATE_RINGING, AST_STATE_UP, ast_streamfile(), ast_verb, ast_waitstream(), agent_pvt::chan, CLEANUP, agent_pvt::lock, LOG_DEBUG, LOG_WARNING, ast_channel::nativeformats, agent_pvt::pending, and ast_channel::tech_pvt.
00830 { 00831 struct agent_pvt *p = ast->tech_pvt; 00832 int res; 00833 int newstate=0; 00834 00835 ast_mutex_lock(&p->lock); 00836 p->acknowledged = 0; 00837 00838 if (p->pending) { 00839 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); 00840 ast_mutex_unlock(&p->lock); 00841 ast_setstate(ast, AST_STATE_DIALING); 00842 return 0; 00843 } 00844 00845 ast_assert(p->chan != NULL); 00846 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); 00847 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language); 00848 00849 ast_mutex_unlock(&p->lock); 00850 00851 res = ast_streamfile(p->chan, beep, p->chan->language); 00852 ast_debug(3, "Played beep, result '%d'\n", res); 00853 if (!res) { 00854 res = ast_waitstream(p->chan, ""); 00855 ast_debug(3, "Waited for stream, result '%d'\n", res); 00856 } 00857 00858 ast_mutex_lock(&p->lock); 00859 00860 if (!res) { 00861 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats)); 00862 ast_debug(3, "Set read format, result '%d'\n", res); 00863 if (res) 00864 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); 00865 } 00866 00867 if (!res) { 00868 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); 00869 ast_debug(3, "Set write format, result '%d'\n", res); 00870 if (res) 00871 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); 00872 } 00873 if(!res) { 00874 /* Call is immediately up, or might need ack */ 00875 if (p->ackcall) { 00876 newstate = AST_STATE_RINGING; 00877 } else { 00878 newstate = AST_STATE_UP; 00879 if (recordagentcalls) 00880 agent_start_monitoring(ast, 0); 00881 p->acknowledged = 1; 00882 } 00883 } 00884 CLEANUP(ast, p); 00885 ast_mutex_unlock(&p->lock); 00886 if (newstate) 00887 ast_setstate(ast, newstate); 00888 return res ? -1 : 0; 00889 }
static int agent_cleanup | ( | struct agent_pvt * | p | ) | [static] |
Deletes an agent after doing some clean up. Further documentation: How safe is this function ? What state should the agent be to be cleaned.
p | Agent to be deleted. |
Definition at line 544 of file chan_agent.c.
References agent_pvt_destroy(), agent_pvt::app_complete_cond, agent_pvt::app_lock_flag, agent_pvt::app_sleep_cond, ast_channel_release(), ast_cond_signal, ast_mutex_lock, ast_mutex_unlock, agent_pvt::chan, agent_pvt::dead, agent_pvt::lock, agent_pvt::owner, and ast_channel::tech_pvt.
Referenced by check_availability().
00545 { 00546 struct ast_channel *chan; 00547 00548 ast_mutex_lock(&p->lock); 00549 chan = p->owner; 00550 p->owner = NULL; 00551 /* Release ownership of the agent to other threads (presumably running the login app). */ 00552 p->app_sleep_cond = 1; 00553 p->app_lock_flag = 0; 00554 ast_cond_signal(&p->app_complete_cond); 00555 if (chan) { 00556 chan->tech_pvt = NULL; 00557 chan = ast_channel_release(chan); 00558 } 00559 if (p->dead) { 00560 ast_mutex_unlock(&p->lock); 00561 agent_pvt_destroy(p); 00562 } else { 00563 ast_mutex_unlock(&p->lock); 00564 } 00565 return 0; 00566 }
static int agent_cont_sleep | ( | void * | data | ) | [static] |
Definition at line 976 of file chan_agent.c.
References agent_pvt::app_sleep_cond, ast_debug, ast_mutex_lock, ast_mutex_unlock, ast_tvdiff_ms(), ast_tvnow(), agent_pvt::lastdisc, and agent_pvt::lock.
Referenced by login_exec().
00977 { 00978 struct agent_pvt *p; 00979 int res; 00980 00981 p = (struct agent_pvt *) data; 00982 00983 ast_mutex_lock(&p->lock); 00984 res = p->app_sleep_cond; 00985 if (res && p->lastdisc.tv_sec) { 00986 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { 00987 res = 0; 00988 } 00989 } 00990 ast_mutex_unlock(&p->lock); 00991 00992 if (!res) { 00993 ast_debug(5, "agent_cont_sleep() returning %d\n", res); 00994 } 00995 00996 return res; 00997 }
static int agent_devicestate | ( | void * | data | ) | [static] |
Part of PBX channel interface.
Definition at line 2252 of file chan_agent.c.
References agent_pvt::agent, AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, agent_pvt::chan, agent_pvt::deferlogoff, agent_pvt::lastdisc, agent_pvt::lock, agent_pvt::owner, and agent_pvt::pending.
02253 { 02254 struct agent_pvt *p; 02255 const char *device = data; 02256 int res = AST_DEVICE_INVALID; 02257 02258 if (device[0] == '@' || device[0] == ':') { 02259 /* Device state of groups not supported. */ 02260 return AST_DEVICE_INVALID; 02261 } 02262 02263 /* Want device state of a specific agent. */ 02264 AST_LIST_LOCK(&agents); 02265 AST_LIST_TRAVERSE(&agents, p, list) { 02266 ast_mutex_lock(&p->lock); 02267 if (!p->pending && !strcmp(device, p->agent)) { 02268 if (p->owner) { 02269 res = AST_DEVICE_BUSY; 02270 } else if (p->chan) { 02271 if (p->lastdisc.tv_sec || p->deferlogoff) { 02272 /* Agent is in wrapup time so unavailable for another call. */ 02273 res = AST_DEVICE_INUSE; 02274 } else { 02275 res = AST_DEVICE_NOT_INUSE; 02276 } 02277 } else { 02278 res = AST_DEVICE_UNAVAILABLE; 02279 } 02280 ast_mutex_unlock(&p->lock); 02281 break; 02282 } 02283 ast_mutex_unlock(&p->lock); 02284 } 02285 AST_LIST_UNLOCK(&agents); 02286 return res; 02287 }
static int agent_digit_begin | ( | struct ast_channel * | ast, | |
char | digit | |||
) | [static] |
Definition at line 807 of file chan_agent.c.
References ast_mutex_lock, ast_mutex_unlock, ast_senddigit_begin(), agent_pvt::chan, agent_pvt::lock, and ast_channel::tech_pvt.
00808 { 00809 struct agent_pvt *p = ast->tech_pvt; 00810 ast_mutex_lock(&p->lock); 00811 if (p->chan) { 00812 ast_senddigit_begin(p->chan, digit); 00813 } 00814 ast_mutex_unlock(&p->lock); 00815 return 0; 00816 }
static int agent_digit_end | ( | struct ast_channel * | ast, | |
char | digit, | |||
unsigned int | duration | |||
) | [static] |
Definition at line 818 of file chan_agent.c.
References ast_mutex_lock, ast_mutex_unlock, ast_senddigit_end(), agent_pvt::chan, agent_pvt::lock, and ast_channel::tech_pvt.
00819 { 00820 struct agent_pvt *p = ast->tech_pvt; 00821 ast_mutex_lock(&p->lock); 00822 if (p->chan) { 00823 ast_senddigit_end(p->chan, digit, duration); 00824 } 00825 ast_mutex_unlock(&p->lock); 00826 return 0; 00827 }
static int agent_fixup | ( | struct ast_channel * | oldchan, | |
struct ast_channel * | newchan | |||
) | [static] |
Definition at line 773 of file chan_agent.c.
References ast_log(), ast_mutex_lock, ast_mutex_unlock, agent_pvt::lock, LOG_WARNING, agent_pvt::owner, and ast_channel::tech_pvt.
00774 { 00775 struct agent_pvt *p = newchan->tech_pvt; 00776 ast_mutex_lock(&p->lock); 00777 if (p->owner != oldchan) { 00778 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); 00779 ast_mutex_unlock(&p->lock); 00780 return -1; 00781 } 00782 p->owner = newchan; 00783 ast_mutex_unlock(&p->lock); 00784 return 0; 00785 }
struct ast_channel * agent_get_base_channel | ( | struct ast_channel * | chan | ) | [static, read] |
return the channel or base channel if one exists. This function assumes the channel it is called on is already locked
Definition at line 892 of file chan_agent.c.
References ast_log(), agent_pvt::chan, LOG_ERROR, and ast_channel::tech_pvt.
00893 { 00894 struct agent_pvt *p; 00895 struct ast_channel *base = chan; 00896 00897 /* chan is locked by the calling function */ 00898 if (!chan || !chan->tech_pvt) { 00899 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL); 00900 return NULL; 00901 } 00902 p = chan->tech_pvt; 00903 if (p->chan) 00904 base = p->chan; 00905 return base; 00906 }
static int agent_hangup | ( | struct ast_channel * | ast | ) | [static] |
Definition at line 908 of file chan_agent.c.
References ast_channel::_bridge, ast_channel::_state, agent_pvt::abouttograb, agent_pvt::acknowledged, agent_pvt_destroy(), agent_pvt::app_complete_cond, agent_pvt::app_lock_flag, agent_pvt::app_sleep_cond, ast_channel_ref, ast_channel_unref, ast_cond_signal, AST_CONTROL_HOLD, ast_debug, ast_indicate_data(), AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_samp2tv(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_state2str(), ast_strdupa, ast_strlen_zero(), ast_tvadd(), ast_tvnow(), agent_pvt::chan, agent_pvt::dead, agent_pvt::lastdisc, agent_pvt::lock, agent_pvt::loginstart, agent_pvt::moh, agent_pvt::owner, agent_pvt::pending, S_OR, agent_pvt::start, ast_channel::tech_pvt, and agent_pvt::wrapuptime.
00909 { 00910 struct agent_pvt *p = ast->tech_pvt; 00911 struct ast_channel *indicate_chan = NULL; 00912 char *tmp_moh; /* moh buffer for indicating after unlocking p */ 00913 00914 if (p->pending) { 00915 AST_LIST_LOCK(&agents); 00916 AST_LIST_REMOVE(&agents, p, list); 00917 AST_LIST_UNLOCK(&agents); 00918 } 00919 00920 ast_mutex_lock(&p->lock); 00921 p->owner = NULL; 00922 ast->tech_pvt = NULL; 00923 p->acknowledged = 0; 00924 00925 /* if they really are hung up then set start to 0 so the test 00926 * later if we're called on an already downed channel 00927 * doesn't cause an agent to be logged out like when 00928 * agent_request() is followed immediately by agent_hangup() 00929 * as in apps/app_chanisavail.c:chanavail_exec() 00930 */ 00931 00932 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state)); 00933 p->start = 0; 00934 if (p->chan) { 00935 p->chan->_bridge = NULL; 00936 /* If they're dead, go ahead and hang up on the agent now */ 00937 if (p->dead) { 00938 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); 00939 } else if (p->loginstart) { 00940 indicate_chan = ast_channel_ref(p->chan); 00941 tmp_moh = ast_strdupa(p->moh); 00942 } 00943 } 00944 ast_mutex_unlock(&p->lock); 00945 00946 if (indicate_chan) { 00947 ast_indicate_data(indicate_chan, AST_CONTROL_HOLD, 00948 S_OR(tmp_moh, NULL), 00949 !ast_strlen_zero(tmp_moh) ? strlen(tmp_moh) + 1 : 0); 00950 indicate_chan = ast_channel_unref(indicate_chan); 00951 } 00952 00953 ast_mutex_lock(&p->lock); 00954 if (p->abouttograb) { 00955 /* Let the "about to grab" thread know this isn't valid anymore, and let it 00956 kill it later */ 00957 p->abouttograb = 0; 00958 } else if (p->dead) { 00959 ast_mutex_unlock(&p->lock); 00960 agent_pvt_destroy(p); 00961 return 0; 00962 } else { 00963 /* Store last disconnect time */ 00964 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); 00965 } 00966 00967 /* Release ownership of the agent to other threads (presumably running the login app). */ 00968 p->app_sleep_cond = 1; 00969 p->app_lock_flag = 0; 00970 ast_cond_signal(&p->app_complete_cond); 00971 00972 ast_mutex_unlock(&p->lock); 00973 return 0; 00974 }
static int agent_indicate | ( | struct ast_channel * | ast, | |
int | condition, | |||
const void * | data, | |||
size_t | datalen | |||
) | [static] |
Definition at line 787 of file chan_agent.c.
References ast_channel_lock, ast_channel_unlock, ast_check_hangup(), ast_mutex_lock, ast_mutex_unlock, agent_pvt::chan, ast_channel_tech::indicate, agent_pvt::lock, ast_channel::tech, and ast_channel::tech_pvt.
00788 { 00789 struct agent_pvt *p = ast->tech_pvt; 00790 int res = -1; 00791 00792 ast_mutex_lock(&p->lock); 00793 if (p->chan && !ast_check_hangup(p->chan)) { 00794 ast_channel_unlock(ast); 00795 ast_channel_lock(p->chan); 00796 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1; 00797 ast_channel_unlock(p->chan); 00798 ast_mutex_unlock(&p->lock); 00799 ast_channel_lock(ast); 00800 } else { 00801 ast_mutex_unlock(&p->lock); 00802 res = 0; 00803 } 00804 return res; 00805 }
static struct ast_channel* agent_lock_owner | ( | struct agent_pvt * | pvt | ) | [static, read] |
Locks the owning channel for a LOCKED pvt while obeying locking order. The pvt must enter this function locked and will be returned locked, but this function will unlock the pvt for a short time, so it can't be used while expecting the pvt to remain static. If function returns a non NULL channel, it will need to be unlocked and unrefed once it is no longer needed.
pvt | Pointer to the LOCKED agent_pvt for which the owner is needed locked channel which owns the pvt at the time of completion. NULL if not available. |
Definition at line 382 of file chan_agent.c.
References ast_channel_lock, ast_channel_ref, ast_channel_unlock, ast_channel_unref, ast_mutex_lock, ast_mutex_unlock, agent_pvt::lock, and agent_pvt::owner.
Referenced by action_agents(), agent_logoff(), agent_read(), agents_data_provider_get(), agents_show(), and agents_show_online().
00383 { 00384 struct ast_channel *owner; 00385 00386 for (;;) { 00387 if (!pvt->owner) { /* No owner. Nothing to do. */ 00388 return NULL; 00389 } 00390 00391 /* If we don't ref the owner, it could be killed when we unlock the pvt. */ 00392 owner = ast_channel_ref(pvt->owner); 00393 00394 /* Locking order requires us to lock channel, then pvt. */ 00395 ast_mutex_unlock(&pvt->lock); 00396 ast_channel_lock(owner); 00397 ast_mutex_lock(&pvt->lock); 00398 00399 /* Check if owner changed during pvt unlock period */ 00400 if (owner != pvt->owner) { /* Channel changed. Unref and do another pass. */ 00401 ast_channel_unlock(owner); 00402 owner = ast_channel_unref(owner); 00403 } else { /* Channel stayed the same. Return it. */ 00404 return owner; 00405 } 00406 } 00407 }
static int agent_logoff | ( | const char * | agent, | |
int | soft | |||
) | [static] |
Definition at line 1574 of file chan_agent.c.
References agent_pvt::agent, agent_lock_owner(), ast_channel_trylock, ast_channel_unlock, ast_channel_unref, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, agent_pvt::chan, DEADLOCK_AVOIDANCE, agent_pvt::deferlogoff, agent_pvt::lock, and agent_pvt::owner.
Referenced by action_agent_logoff(), and agent_logoff_cmd().
01575 { 01576 struct agent_pvt *p; 01577 int ret = -1; /* Return -1 if no agent if found */ 01578 01579 AST_LIST_LOCK(&agents); 01580 AST_LIST_TRAVERSE(&agents, p, list) { 01581 if (!strcasecmp(p->agent, agent)) { 01582 ret = 0; 01583 if (p->owner || p->chan) { 01584 if (!soft) { 01585 struct ast_channel *owner; 01586 ast_mutex_lock(&p->lock); 01587 owner = agent_lock_owner(p); 01588 01589 if (owner) { 01590 ast_softhangup(owner, AST_SOFTHANGUP_EXPLICIT); 01591 ast_channel_unlock(owner); 01592 owner = ast_channel_unref(owner); 01593 } 01594 01595 while (p->chan && ast_channel_trylock(p->chan)) { 01596 DEADLOCK_AVOIDANCE(&p->lock); 01597 } 01598 if (p->chan) { 01599 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); 01600 ast_channel_unlock(p->chan); 01601 } 01602 01603 ast_mutex_unlock(&p->lock); 01604 } else 01605 p->deferlogoff = 1; 01606 } 01607 break; 01608 } 01609 } 01610 AST_LIST_UNLOCK(&agents); 01611 01612 return ret; 01613 }
static char* agent_logoff_cmd | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1615 of file chan_agent.c.
References agent_pvt::agent, agent_logoff(), ast_cli_args::argc, ast_cli_args::argv, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_agent_logoff_cmd(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.
01616 { 01617 int ret; 01618 const char *agent; 01619 01620 switch (cmd) { 01621 case CLI_INIT: 01622 e->command = "agent logoff"; 01623 e->usage = 01624 "Usage: agent logoff <channel> [soft]\n" 01625 " Sets an agent as no longer logged in.\n" 01626 " If 'soft' is specified, do not hangup existing calls.\n"; 01627 return NULL; 01628 case CLI_GENERATE: 01629 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n); 01630 } 01631 01632 if (a->argc < 3 || a->argc > 4) 01633 return CLI_SHOWUSAGE; 01634 if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) 01635 return CLI_SHOWUSAGE; 01636 01637 agent = a->argv[2] + 6; 01638 ret = agent_logoff(agent, a->argc == 4); 01639 if (ret == 0) 01640 ast_cli(a->fd, "Logging out %s\n", agent); 01641 01642 return CLI_SUCCESS; 01643 }
static struct ast_channel* agent_new | ( | struct agent_pvt * | p, | |
int | state, | |||
const char * | linkedid | |||
) | [static, read] |
Create new agent channel.
Definition at line 1063 of file chan_agent.c.
References agent_pvt::agent, agent_tech, ast_channel_alloc, ast_copy_string(), AST_FORMAT_SLINEAR, ast_log(), ast_random(), ast_string_field_set, agent_pvt::chan, ast_channel::context, ast_channel::exten, language, LOG_WARNING, ast_channel::nativeformats, agent_pvt::owner, agent_pvt::pending, ast_channel::priority, ast_channel::rawreadformat, ast_channel::rawwriteformat, ast_channel::readformat, ast_channel::tech, ast_channel::tech_pvt, and ast_channel::writeformat.
Referenced by agent_request(), and check_availability().
01064 { 01065 struct ast_channel *tmp; 01066 #if 0 01067 if (!p->chan) { 01068 ast_log(LOG_WARNING, "No channel? :(\n"); 01069 return NULL; 01070 } 01071 #endif 01072 if (p->pending) 01073 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff); 01074 else 01075 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent); 01076 if (!tmp) { 01077 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); 01078 return NULL; 01079 } 01080 01081 tmp->tech = &agent_tech; 01082 if (p->chan) { 01083 tmp->nativeformats = p->chan->nativeformats; 01084 tmp->writeformat = p->chan->writeformat; 01085 tmp->rawwriteformat = p->chan->writeformat; 01086 tmp->readformat = p->chan->readformat; 01087 tmp->rawreadformat = p->chan->readformat; 01088 ast_string_field_set(tmp, language, p->chan->language); 01089 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); 01090 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); 01091 /* XXX Is this really all we copy form the originating channel?? */ 01092 } else { 01093 tmp->nativeformats = AST_FORMAT_SLINEAR; 01094 tmp->writeformat = AST_FORMAT_SLINEAR; 01095 tmp->rawwriteformat = AST_FORMAT_SLINEAR; 01096 tmp->readformat = AST_FORMAT_SLINEAR; 01097 tmp->rawreadformat = AST_FORMAT_SLINEAR; 01098 } 01099 /* Safe, agentlock already held */ 01100 tmp->tech_pvt = p; 01101 p->owner = tmp; 01102 tmp->priority = 1; 01103 return tmp; 01104 }
static void agent_pvt_destroy | ( | struct agent_pvt * | doomed | ) | [static] |
Definition at line 417 of file chan_agent.c.
References agent_pvt::app_complete_cond, ast_cond_destroy, ast_free, ast_mutex_destroy, agent_pvt::lock, and agent_pvt::login_wait_cond.
Referenced by agent_cleanup(), agent_hangup(), agent_request(), login_exec(), and read_agent_config().
00418 { 00419 ast_mutex_destroy(&doomed->lock); 00420 ast_cond_destroy(&doomed->app_complete_cond); 00421 ast_cond_destroy(&doomed->login_wait_cond); 00422 ast_free(doomed); 00423 }
static struct ast_frame * agent_read | ( | struct ast_channel * | ast | ) | [static, read] |
Definition at line 607 of file chan_agent.c.
References ast_channel::_bridge, ast_channel::_state, agent_pvt::acceptdtmf, agent_pvt::ackcall, agent_pvt::acknowledged, agent_lock_owner(), agent_start_monitoring(), AST_AGENT_FD, ast_channel_trylock, ast_channel_unlock, ast_channel_unref, AST_CONTROL_ANSWER, ast_copy_flags, ast_debug, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_null_frame, ast_read(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, AST_STATE_UP, AST_TIMING_FD, ast_verb, agent_pvt::autologoff, agent_pvt::chan, CHECK_FORMATS, CLEANUP, DEADLOCK_AVOIDANCE, agent_pvt::enddtmf, f, ast_channel::fdno, ast_frame::frametype, ast_frame_subclass::integer, agent_pvt::lock, LOG_NOTICE, agent_pvt::name, agent_pvt::owner, agent_pvt::start, ast_frame::subclass, ast_channel::tech, ast_channel::tech_pvt, and ast_channel_tech::type.
00608 { 00609 struct agent_pvt *p = ast->tech_pvt; 00610 struct ast_frame *f = NULL; 00611 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } }; 00612 int cur_time = time(NULL); 00613 struct ast_channel *owner; 00614 00615 ast_mutex_lock(&p->lock); 00616 owner = agent_lock_owner(p); 00617 00618 CHECK_FORMATS(ast, p); 00619 if (!p->start) { 00620 p->start = cur_time; 00621 } 00622 if (p->chan) { 00623 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); 00624 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno; 00625 f = ast_read(p->chan); 00626 ast->fdno = -1; 00627 } else 00628 f = &ast_null_frame; 00629 if (f) { 00630 /* if acknowledgement is not required, and the channel is up, we may have missed 00631 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */ 00632 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) { 00633 p->acknowledged = 1; 00634 } 00635 00636 if (!p->acknowledged) { 00637 int howlong = cur_time - p->start; 00638 if (p->autologoff && (howlong >= p->autologoff)) { 00639 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); 00640 if (owner || p->chan) { 00641 if (owner) { 00642 ast_softhangup(owner, AST_SOFTHANGUP_EXPLICIT); 00643 ast_channel_unlock(owner); 00644 owner = ast_channel_unref(owner); 00645 } 00646 00647 while (p->chan && ast_channel_trylock(p->chan)) { 00648 DEADLOCK_AVOIDANCE(&p->lock); 00649 } 00650 if (p->chan) { 00651 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); 00652 ast_channel_unlock(p->chan); 00653 } 00654 } 00655 } 00656 } 00657 switch (f->frametype) { 00658 case AST_FRAME_CONTROL: 00659 if (f->subclass.integer == AST_CONTROL_ANSWER) { 00660 if (p->ackcall) { 00661 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf); 00662 /* Don't pass answer along */ 00663 ast_frfree(f); 00664 f = &ast_null_frame; 00665 } else { 00666 p->acknowledged = 1; 00667 /* Use the builtin answer frame for the 00668 recording start check below. */ 00669 ast_frfree(f); 00670 f = &answer_frame; 00671 } 00672 } 00673 break; 00674 case AST_FRAME_DTMF_BEGIN: 00675 /*ignore DTMF begin's as it can cause issues with queue announce files*/ 00676 if((!p->acknowledged && f->subclass.integer == p->acceptdtmf) || (f->subclass.integer == p->enddtmf && endcall)){ 00677 ast_frfree(f); 00678 f = &ast_null_frame; 00679 } 00680 break; 00681 case AST_FRAME_DTMF_END: 00682 if (!p->acknowledged && (f->subclass.integer == p->acceptdtmf)) { 00683 if (p->chan) { 00684 ast_verb(3, "%s acknowledged\n", p->chan->name); 00685 } 00686 p->acknowledged = 1; 00687 ast_frfree(f); 00688 f = &answer_frame; 00689 } else if (f->subclass.integer == p->enddtmf && endcall) { 00690 /* terminates call */ 00691 ast_frfree(f); 00692 f = NULL; 00693 } 00694 break; 00695 case AST_FRAME_VOICE: 00696 case AST_FRAME_VIDEO: 00697 /* don't pass voice or video until the call is acknowledged */ 00698 if (!p->acknowledged) { 00699 ast_frfree(f); 00700 f = &ast_null_frame; 00701 } 00702 default: 00703 /* pass everything else on through */ 00704 break; 00705 } 00706 } 00707 00708 if (owner) { 00709 ast_channel_unlock(owner); 00710 owner = ast_channel_unref(owner); 00711 } 00712 00713 CLEANUP(ast,p); 00714 if (p->chan && !p->chan->_bridge) { 00715 if (strcasecmp(p->chan->tech->type, "Local")) { 00716 p->chan->_bridge = ast; 00717 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); 00718 } 00719 } 00720 ast_mutex_unlock(&p->lock); 00721 if (recordagentcalls && f == &answer_frame) 00722 agent_start_monitoring(ast,0); 00723 return f; 00724 }
static struct ast_channel * agent_request | ( | const char * | type, | |
format_t | format, | |||
const struct ast_channel * | requestor, | |||
void * | data, | |||
int * | cause | |||
) | [static, read] |
Part of the Asterisk PBX interface.
Definition at line 1370 of file chan_agent.c.
References add_agent(), agent_pvt::agent, agent_new(), agent_pvt_destroy(), agent_pvt::app_lock_flag, agent_pvt::app_sleep_cond, AST_CAUSE_BUSY, AST_CAUSE_UNREGISTERED, ast_cond_wait, AST_CONTROL_UNHOLD, ast_debug, ast_hangup(), ast_indicate(), AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_null_frame, ast_queue_frame(), AST_STATE_DOWN, ast_tv(), ast_tvdiff_ms(), ast_tvnow(), agent_pvt::chan, agent_pvt::group, agent_pvt::lastdisc, agent_pvt::lock, agent_pvt::login_wait_cond, agent_pvt::loginstart, agent_pvt::owner, and agent_pvt::pending.
01371 { 01372 struct agent_pvt *p; 01373 struct ast_channel *chan = NULL; 01374 char *s; 01375 ast_group_t groupmatch; 01376 int groupoff; 01377 int waitforagent=0; 01378 int hasagent = 0; 01379 struct timeval now; 01380 01381 s = data; 01382 if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) { 01383 groupmatch = (1 << groupoff); 01384 } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) { 01385 groupmatch = (1 << groupoff); 01386 waitforagent = 1; 01387 } else 01388 groupmatch = 0; 01389 01390 /* Check actual logged in agents first */ 01391 AST_LIST_LOCK(&agents); 01392 AST_LIST_TRAVERSE(&agents, p, list) { 01393 ast_mutex_lock(&p->lock); 01394 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { 01395 if (p->chan) { 01396 hasagent++; 01397 } 01398 now = ast_tvnow(); 01399 if (p->loginstart 01400 && (!p->lastdisc.tv_sec || ast_tvdiff_ms(now, p->lastdisc) > 0)) { 01401 p->lastdisc = ast_tv(0, 0); 01402 /* Agent must be registered, but not have any active call, and not be in a waiting state */ 01403 if (!p->owner && p->chan) { 01404 /* Fixed agent */ 01405 chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); 01406 } 01407 if (chan) { 01408 ast_mutex_unlock(&p->lock); 01409 break; 01410 } 01411 } 01412 } 01413 ast_mutex_unlock(&p->lock); 01414 } 01415 01416 if (!chan && waitforagent) { 01417 /* No agent available -- but we're requesting to wait for one. 01418 Allocate a place holder */ 01419 if (hasagent) { 01420 ast_debug(1, "Creating place holder for '%s'\n", s); 01421 p = add_agent(data, 1); 01422 if (p) { 01423 p->group = groupmatch; 01424 chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); 01425 if (!chan) { 01426 AST_LIST_REMOVE(&agents, p, list); 01427 agent_pvt_destroy(p); 01428 } 01429 } 01430 } else { 01431 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s); 01432 } 01433 } 01434 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED; 01435 AST_LIST_UNLOCK(&agents); 01436 01437 if (chan) { 01438 ast_mutex_lock(&p->lock); 01439 if (p->pending) { 01440 ast_mutex_unlock(&p->lock); 01441 return chan; 01442 } 01443 01444 if (!p->chan) { 01445 ast_debug(1, "Agent disconnected before we could connect the call\n"); 01446 ast_mutex_unlock(&p->lock); 01447 ast_hangup(chan); 01448 *cause = AST_CAUSE_UNREGISTERED; 01449 return NULL; 01450 } 01451 01452 /* we need to take control of the channel from the login app 01453 * thread */ 01454 p->app_sleep_cond = 0; 01455 p->app_lock_flag = 1; 01456 ast_queue_frame(p->chan, &ast_null_frame); 01457 ast_cond_wait(&p->login_wait_cond, &p->lock); 01458 01459 if (!p->chan) { 01460 ast_debug(1, "Agent disconnected while we were connecting the call\n"); 01461 ast_mutex_unlock(&p->lock); 01462 ast_hangup(chan); 01463 *cause = AST_CAUSE_UNREGISTERED; 01464 return NULL; 01465 } 01466 01467 ast_indicate(p->chan, AST_CONTROL_UNHOLD); 01468 ast_mutex_unlock(&p->lock); 01469 } 01470 01471 return chan; 01472 }
static int agent_sendhtml | ( | struct ast_channel * | ast, | |
int | subclass, | |||
const char * | data, | |||
int | datalen | |||
) | [static] |
Definition at line 726 of file chan_agent.c.
References ast_channel_sendhtml(), ast_mutex_lock, ast_mutex_unlock, agent_pvt::chan, agent_pvt::lock, and ast_channel::tech_pvt.
00727 { 00728 struct agent_pvt *p = ast->tech_pvt; 00729 int res = -1; 00730 ast_mutex_lock(&p->lock); 00731 if (p->chan) 00732 res = ast_channel_sendhtml(p->chan, subclass, data, datalen); 00733 ast_mutex_unlock(&p->lock); 00734 return res; 00735 }
static int agent_sendtext | ( | struct ast_channel * | ast, | |
const char * | text | |||
) | [static] |
Definition at line 737 of file chan_agent.c.
References ast_mutex_lock, ast_mutex_unlock, ast_sendtext(), agent_pvt::chan, agent_pvt::lock, and ast_channel::tech_pvt.
00738 { 00739 struct agent_pvt *p = ast->tech_pvt; 00740 int res = -1; 00741 ast_mutex_lock(&p->lock); 00742 if (p->chan) 00743 res = ast_sendtext(p->chan, text); 00744 ast_mutex_unlock(&p->lock); 00745 return res; 00746 }
static int agent_start_monitoring | ( | struct ast_channel * | ast, | |
int | needlock | |||
) | [static] |
Definition at line 602 of file chan_agent.c.
References __agent_start_monitoring(), and ast_channel::tech_pvt.
Referenced by agent_call(), and agent_read().
00603 { 00604 return __agent_start_monitoring(ast, ast->tech_pvt, needlock); 00605 }
static int agent_write | ( | struct ast_channel * | ast, | |
struct ast_frame * | f | |||
) | [static] |
Definition at line 748 of file chan_agent.c.
References ast_debug, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_mutex_lock, ast_mutex_unlock, ast_write(), agent_pvt::chan, CHECK_FORMATS, CLEANUP, ast_frame_subclass::codec, ast_frame::frametype, agent_pvt::lock, ast_frame::subclass, ast_channel::tech_pvt, and ast_channel::writeformat.
00749 { 00750 struct agent_pvt *p = ast->tech_pvt; 00751 int res = -1; 00752 CHECK_FORMATS(ast, p); 00753 ast_mutex_lock(&p->lock); 00754 if (!p->chan) 00755 res = 0; 00756 else { 00757 if ((f->frametype != AST_FRAME_VOICE) || 00758 (f->frametype != AST_FRAME_VIDEO) || 00759 (f->subclass.codec == p->chan->writeformat)) { 00760 res = ast_write(p->chan, f); 00761 } else { 00762 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n", 00763 f->frametype == AST_FRAME_VOICE ? "audio" : "video", 00764 ast->name, p->chan->name); 00765 res = 0; 00766 } 00767 } 00768 CLEANUP(ast, p); 00769 ast_mutex_unlock(&p->lock); 00770 return res; 00771 }
static int agentmonitoroutgoing_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Called by the AgentMonitorOutgoing application (from the dial plan).
chan | ||
data |
Definition at line 2199 of file chan_agent.c.
References __agent_start_monitoring(), agent_pvt::agent, ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_AGENT, AST_MAX_BUF, ast_strlen_zero(), ast_channel::caller, ast_channel::cdr, ast_cdr::channel, GETAGENTBYCALLERID, ast_party_caller::id, LOG_WARNING, ast_party_id::number, pbx_builtin_getvar_helper(), ast_party_number::str, and ast_party_number::valid.
Referenced by load_module().
02200 { 02201 int exitifnoagentid = 0; 02202 int nowarnings = 0; 02203 int changeoutgoing = 0; 02204 int res = 0; 02205 char agent[AST_MAX_AGENT]; 02206 02207 if (data) { 02208 if (strchr(data, 'd')) 02209 exitifnoagentid = 1; 02210 if (strchr(data, 'n')) 02211 nowarnings = 1; 02212 if (strchr(data, 'c')) 02213 changeoutgoing = 1; 02214 } 02215 if (chan->caller.id.number.valid 02216 && !ast_strlen_zero(chan->caller.id.number.str)) { 02217 const char *tmp; 02218 char agentvar[AST_MAX_BUF]; 02219 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, 02220 chan->caller.id.number.str); 02221 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) { 02222 struct agent_pvt *p; 02223 ast_copy_string(agent, tmp, sizeof(agent)); 02224 AST_LIST_LOCK(&agents); 02225 AST_LIST_TRAVERSE(&agents, p, list) { 02226 if (!strcasecmp(p->agent, tmp)) { 02227 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); 02228 __agent_start_monitoring(chan, p, 1); 02229 break; 02230 } 02231 } 02232 AST_LIST_UNLOCK(&agents); 02233 02234 } else { 02235 res = -1; 02236 if (!nowarnings) 02237 ast_log(LOG_WARNING, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar); 02238 } 02239 } else { 02240 res = -1; 02241 if (!nowarnings) 02242 ast_log(LOG_WARNING, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n"); 02243 } 02244 if (res) { 02245 if (exitifnoagentid) 02246 return res; 02247 } 02248 return 0; 02249 }
static int agents_data_provider_get | ( | const struct ast_data_search * | search, | |
struct ast_data * | data_root | |||
) | [static] |
Definition at line 2383 of file chan_agent.c.
References agent_pvt::agent, agent_lock_owner(), ast_bridged_channel(), ast_channel_data_add_structure(), ast_channel_unlock, ast_channel_unref, ast_data_add_bool(), ast_data_add_node(), ast_data_add_str(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, agent_pvt::chan, agent_pvt::lock, agent_pvt::moh, agent_pvt::owner, and agent_pvt::pending.
02385 { 02386 struct agent_pvt *p; 02387 struct ast_data *data_agent, *data_channel, *data_talkingto; 02388 02389 AST_LIST_LOCK(&agents); 02390 AST_LIST_TRAVERSE(&agents, p, list) { 02391 struct ast_channel *owner; 02392 02393 data_agent = ast_data_add_node(data_root, "agent"); 02394 if (!data_agent) { 02395 continue; 02396 } 02397 02398 ast_mutex_lock(&p->lock); 02399 owner = agent_lock_owner(p); 02400 02401 if (!(p->pending)) { 02402 ast_data_add_str(data_agent, "id", p->agent); 02403 ast_data_add_structure(agent_pvt, data_agent, p); 02404 02405 ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0); 02406 if (p->chan) { 02407 data_channel = ast_data_add_node(data_agent, "loggedon"); 02408 if (!data_channel) { 02409 ast_mutex_unlock(&p->lock); 02410 ast_data_remove_node(data_root, data_agent); 02411 if (owner) { 02412 ast_channel_unlock(owner); 02413 owner = ast_channel_unref(owner); 02414 } 02415 continue; 02416 } 02417 ast_channel_data_add_structure(data_channel, p->chan, 0); 02418 if (owner && ast_bridged_channel(owner)) { 02419 data_talkingto = ast_data_add_node(data_agent, "talkingto"); 02420 if (!data_talkingto) { 02421 ast_mutex_unlock(&p->lock); 02422 ast_data_remove_node(data_root, data_agent); 02423 if (owner) { 02424 ast_channel_unlock(owner); 02425 owner = ast_channel_unref(owner); 02426 } 02427 continue; 02428 } 02429 ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(owner), 0); 02430 } 02431 } else { 02432 ast_data_add_node(data_agent, "talkingto"); 02433 ast_data_add_node(data_agent, "loggedon"); 02434 } 02435 ast_data_add_str(data_agent, "musiconhold", p->moh); 02436 } 02437 02438 if (owner) { 02439 ast_channel_unlock(owner); 02440 owner = ast_channel_unref(owner); 02441 } 02442 02443 ast_mutex_unlock(&p->lock); 02444 02445 /* if this agent doesn't match remove the added agent. */ 02446 if (!ast_data_search_match(search, data_agent)) { 02447 ast_data_remove_node(data_root, data_agent); 02448 } 02449 } 02450 AST_LIST_UNLOCK(&agents); 02451 02452 return 0; 02453 }
static char* agents_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Show agents in cli.
< Number of agents configured
< Number of online agents
< Number of offline agents
Definition at line 1702 of file chan_agent.c.
References agent_pvt::agent, agent_lock_owner(), ast_cli_args::argc, ast_bridged_channel(), ast_channel_unlock, ast_channel_unref, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, AST_MAX_BUF, ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), agent_pvt::chan, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, count_agents(), ast_cli_args::fd, agent_pvt::group, agent_pvt::lock, agent_pvt::moh, agent_pvt::name, agent_pvt::owner, agent_pvt::pending, powerof(), and ast_cli_entry::usage.
01703 { 01704 struct agent_pvt *p; 01705 char username[AST_MAX_BUF]; 01706 char location[AST_MAX_BUF] = ""; 01707 char talkingto[AST_MAX_BUF] = ""; 01708 char music[AST_MAX_BUF]; 01709 int count_agents = 0; /*!< Number of agents configured */ 01710 int online_agents = 0; /*!< Number of online agents */ 01711 int offline_agents = 0; /*!< Number of offline agents */ 01712 01713 switch (cmd) { 01714 case CLI_INIT: 01715 e->command = "agent show"; 01716 e->usage = 01717 "Usage: agent show\n" 01718 " Provides summary information on agents.\n"; 01719 return NULL; 01720 case CLI_GENERATE: 01721 return NULL; 01722 } 01723 01724 if (a->argc != 2) 01725 return CLI_SHOWUSAGE; 01726 01727 AST_LIST_LOCK(&agents); 01728 AST_LIST_TRAVERSE(&agents, p, list) { 01729 struct ast_channel *owner; 01730 ast_mutex_lock(&p->lock); 01731 owner = agent_lock_owner(p); 01732 if (p->pending) { 01733 if (p->group) 01734 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group)); 01735 else 01736 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent); 01737 } else { 01738 if (!ast_strlen_zero(p->name)) 01739 snprintf(username, sizeof(username), "(%s) ", p->name); 01740 else 01741 username[0] = '\0'; 01742 if (p->chan) { 01743 snprintf(location, sizeof(location), "logged in on %s", p->chan->name); 01744 if (owner && ast_bridged_channel(owner)) { 01745 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name); 01746 } else { 01747 strcpy(talkingto, " is idle"); 01748 } 01749 online_agents++; 01750 } else { 01751 strcpy(location, "not logged in"); 01752 talkingto[0] = '\0'; 01753 offline_agents++; 01754 } 01755 if (!ast_strlen_zero(p->moh)) 01756 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh); 01757 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, 01758 username, location, talkingto, music); 01759 count_agents++; 01760 } 01761 01762 if (owner) { 01763 ast_channel_unlock(owner); 01764 owner = ast_channel_unref(owner); 01765 } 01766 ast_mutex_unlock(&p->lock); 01767 } 01768 AST_LIST_UNLOCK(&agents); 01769 if ( !count_agents ) 01770 ast_cli(a->fd, "No Agents are configured in %s\n",config); 01771 else 01772 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents); 01773 ast_cli(a->fd, "\n"); 01774 01775 return CLI_SUCCESS; 01776 }
static char* agents_show_online | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 1779 of file chan_agent.c.
References agent_pvt::agent, agent_lock_owner(), ast_cli_args::argc, ast_bridged_channel(), ast_channel_unlock, ast_channel_unref, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, AST_MAX_BUF, ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), agent_pvt::chan, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, count_agents(), ast_cli_args::fd, agent_pvt::lock, agent_pvt::moh, agent_pvt::name, agent_pvt::owner, and ast_cli_entry::usage.
01780 { 01781 struct agent_pvt *p; 01782 char username[AST_MAX_BUF]; 01783 char location[AST_MAX_BUF] = ""; 01784 char talkingto[AST_MAX_BUF] = ""; 01785 char music[AST_MAX_BUF]; 01786 int count_agents = 0; /* Number of agents configured */ 01787 int online_agents = 0; /* Number of online agents */ 01788 int agent_status = 0; /* 0 means offline, 1 means online */ 01789 01790 switch (cmd) { 01791 case CLI_INIT: 01792 e->command = "agent show online"; 01793 e->usage = 01794 "Usage: agent show online\n" 01795 " Provides a list of all online agents.\n"; 01796 return NULL; 01797 case CLI_GENERATE: 01798 return NULL; 01799 } 01800 01801 if (a->argc != 3) 01802 return CLI_SHOWUSAGE; 01803 01804 AST_LIST_LOCK(&agents); 01805 AST_LIST_TRAVERSE(&agents, p, list) { 01806 struct ast_channel *owner; 01807 01808 agent_status = 0; /* reset it to offline */ 01809 ast_mutex_lock(&p->lock); 01810 owner = agent_lock_owner(p); 01811 01812 if (!ast_strlen_zero(p->name)) 01813 snprintf(username, sizeof(username), "(%s) ", p->name); 01814 else 01815 username[0] = '\0'; 01816 if (p->chan) { 01817 snprintf(location, sizeof(location), "logged in on %s", p->chan->name); 01818 if (owner && ast_bridged_channel(owner)) { 01819 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(owner)->name); 01820 } else { 01821 strcpy(talkingto, " is idle"); 01822 } 01823 agent_status = 1; 01824 online_agents++; 01825 } 01826 01827 if (owner) { 01828 ast_channel_unlock(owner); 01829 owner = ast_channel_unref(owner); 01830 } 01831 01832 if (!ast_strlen_zero(p->moh)) 01833 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh); 01834 if (agent_status) 01835 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music); 01836 count_agents++; 01837 ast_mutex_unlock(&p->lock); 01838 } 01839 AST_LIST_UNLOCK(&agents); 01840 if (!count_agents) 01841 ast_cli(a->fd, "No Agents are configured in %s\n", config); 01842 else 01843 ast_cli(a->fd, "%d agents online\n", online_agents); 01844 ast_cli(a->fd, "\n"); 01845 return CLI_SUCCESS; 01846 }
AST_DATA_STRUCTURE | ( | agent_pvt | , | |
DATA_EXPORT_AGENT | ||||
) |
static AST_LIST_HEAD_STATIC | ( | agents | , | |
agent_pvt | ||||
) | [static] |
Holds the list of agents (loaded form agents.conf).
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"Agent Proxy Channel" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload , |
|||
. | load_pri = AST_MODPRI_CHANNEL_DRIVER , |
|||
. | nonoptreq = "res_monitor,chan_local" | |||
) |
static int check_availability | ( | struct agent_pvt * | newlyavailable, | |
int | needlock | |||
) | [static] |
Definition at line 1270 of file chan_agent.c.
References agent_pvt::abouttograb, agent_pvt::ackcall, agent_pvt::acknowledged, agent_pvt::agent, agent_cleanup(), agent_new(), ast_channel_masquerade(), ast_copy_string(), ast_debug, ast_hangup(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_setstate(), AST_STATE_DOWN, AST_STATE_UP, ast_streamfile(), ast_waitstream(), agent_pvt::chan, ast_channel::context, agent_pvt::group, agent_pvt::lock, agent_pvt::owner, and agent_pvt::pending.
Referenced by login_exec().
01271 { 01272 struct ast_channel *chan=NULL, *parent=NULL; 01273 struct agent_pvt *p; 01274 int res; 01275 01276 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent); 01277 if (needlock) 01278 AST_LIST_LOCK(&agents); 01279 AST_LIST_TRAVERSE(&agents, p, list) { 01280 if (p == newlyavailable) { 01281 continue; 01282 } 01283 ast_mutex_lock(&p->lock); 01284 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { 01285 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent); 01286 /* We found a pending call, time to merge */ 01287 chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL); 01288 parent = p->owner; 01289 p->abouttograb = 1; 01290 ast_mutex_unlock(&p->lock); 01291 break; 01292 } 01293 ast_mutex_unlock(&p->lock); 01294 } 01295 if (needlock) 01296 AST_LIST_UNLOCK(&agents); 01297 if (parent && chan) { 01298 if (newlyavailable->ackcall) { 01299 /* Don't do beep here */ 01300 res = 0; 01301 } else { 01302 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language); 01303 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); 01304 ast_debug(3, "Played beep, result '%d'\n", res); 01305 if (!res) { 01306 res = ast_waitstream(newlyavailable->chan, ""); 01307 ast_debug(1, "Waited for stream, result '%d'\n", res); 01308 } 01309 } 01310 if (!res) { 01311 /* Note -- parent may have disappeared */ 01312 if (p->abouttograb) { 01313 newlyavailable->acknowledged = 1; 01314 /* Safe -- agent lock already held */ 01315 ast_setstate(parent, AST_STATE_UP); 01316 ast_setstate(chan, AST_STATE_UP); 01317 ast_copy_string(parent->context, chan->context, sizeof(parent->context)); 01318 ast_channel_masquerade(parent, chan); 01319 ast_hangup(chan); 01320 p->abouttograb = 0; 01321 } else { 01322 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n"); 01323 agent_cleanup(newlyavailable); 01324 } 01325 } else { 01326 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n"); 01327 agent_cleanup(newlyavailable); 01328 } 01329 } 01330 return 0; 01331 }
static int check_beep | ( | struct agent_pvt * | newlyavailable, | |
int | needlock | |||
) | [static] |
Definition at line 1333 of file chan_agent.c.
References agent_pvt::abouttograb, agent_pvt::agent, ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, ast_streamfile(), ast_waitstream(), agent_pvt::chan, agent_pvt::group, agent_pvt::lock, agent_pvt::owner, and agent_pvt::pending.
Referenced by login_exec().
01334 { 01335 struct agent_pvt *p; 01336 int res=0; 01337 01338 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent); 01339 if (needlock) 01340 AST_LIST_LOCK(&agents); 01341 AST_LIST_TRAVERSE(&agents, p, list) { 01342 if (p == newlyavailable) { 01343 continue; 01344 } 01345 ast_mutex_lock(&p->lock); 01346 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { 01347 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent); 01348 ast_mutex_unlock(&p->lock); 01349 break; 01350 } 01351 ast_mutex_unlock(&p->lock); 01352 } 01353 if (needlock) 01354 AST_LIST_UNLOCK(&agents); 01355 if (p) { 01356 ast_mutex_unlock(&newlyavailable->lock); 01357 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language); 01358 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); 01359 ast_debug(1, "Played beep, result '%d'\n", res); 01360 if (!res) { 01361 res = ast_waitstream(newlyavailable->chan, ""); 01362 ast_debug(1, "Waited for stream, result '%d'\n", res); 01363 } 01364 ast_mutex_lock(&newlyavailable->lock); 01365 } 01366 return res; 01367 }
static char * complete_agent_logoff_cmd | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 1675 of file chan_agent.c.
References agent_pvt::agent, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, AST_MAX_AGENT, ast_strdup, len(), agent_pvt::loginstart, and agent_pvt::name.
Referenced by agent_logoff_cmd().
01676 { 01677 char *ret = NULL; 01678 01679 if (pos == 2) { 01680 struct agent_pvt *p; 01681 char name[AST_MAX_AGENT]; 01682 int which = 0, len = strlen(word); 01683 01684 AST_LIST_LOCK(&agents); 01685 AST_LIST_TRAVERSE(&agents, p, list) { 01686 snprintf(name, sizeof(name), "Agent/%s", p->agent); 01687 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) { 01688 ret = ast_strdup(name); 01689 break; 01690 } 01691 } 01692 AST_LIST_UNLOCK(&agents); 01693 } else if (pos == 3 && state == 0) 01694 return ast_strdup("soft"); 01695 01696 return ret; 01697 }
static struct agent_pvt* find_agent | ( | char * | agentid | ) | [static, read] |
Definition at line 2292 of file chan_agent.c.
References agent_pvt::agent, and AST_LIST_TRAVERSE.
Referenced by function_agent().
02293 { 02294 struct agent_pvt *cur; 02295 02296 AST_LIST_TRAVERSE(&agents, cur, list) { 02297 if (!strcmp(cur->agent, agentid)) 02298 break; 02299 } 02300 02301 return cur; 02302 }
static int function_agent | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 2304 of file chan_agent.c.
References agent_pvt::agent, args, AST_APP_ARG, ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), AST_NONSTANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), agent_pvt::chan, find_agent(), LOG_WARNING, agent_pvt::moh, agent_pvt::name, parse(), agent_pvt::password, and status.
02305 { 02306 char *parse; 02307 AST_DECLARE_APP_ARGS(args, 02308 AST_APP_ARG(agentid); 02309 AST_APP_ARG(item); 02310 ); 02311 char *tmp; 02312 struct agent_pvt *agent; 02313 02314 buf[0] = '\0'; 02315 02316 if (ast_strlen_zero(data)) { 02317 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n"); 02318 return -1; 02319 } 02320 02321 parse = ast_strdupa(data); 02322 02323 AST_NONSTANDARD_APP_ARGS(args, parse, ':'); 02324 if (!args.item) 02325 args.item = "status"; 02326 02327 AST_LIST_LOCK(&agents); 02328 02329 if (!(agent = find_agent(args.agentid))) { 02330 AST_LIST_UNLOCK(&agents); 02331 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid); 02332 return -1; 02333 } 02334 02335 if (!strcasecmp(args.item, "status")) { 02336 char *status = "LOGGEDOUT"; 02337 if (agent->chan) { 02338 status = "LOGGEDIN"; 02339 } 02340 ast_copy_string(buf, status, len); 02341 } else if (!strcasecmp(args.item, "password")) 02342 ast_copy_string(buf, agent->password, len); 02343 else if (!strcasecmp(args.item, "name")) 02344 ast_copy_string(buf, agent->name, len); 02345 else if (!strcasecmp(args.item, "mohclass")) 02346 ast_copy_string(buf, agent->moh, len); 02347 else if (!strcasecmp(args.item, "channel")) { 02348 if (agent->chan) { 02349 ast_channel_lock(agent->chan); 02350 ast_copy_string(buf, agent->chan->name, len); 02351 ast_channel_unlock(agent->chan); 02352 tmp = strrchr(buf, '-'); 02353 if (tmp) 02354 *tmp = '\0'; 02355 } 02356 } else if (!strcasecmp(args.item, "fullchannel")) { 02357 if (agent->chan) { 02358 ast_channel_lock(agent->chan); 02359 ast_copy_string(buf, agent->chan->name, len); 02360 ast_channel_unlock(agent->chan); 02361 } 02362 } else if (!strcasecmp(args.item, "exten")) { 02363 buf[0] = '\0'; 02364 } 02365 02366 AST_LIST_UNLOCK(&agents); 02367 02368 return 0; 02369 }
static int load_module | ( | void | ) | [static] |
Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other things it registers applications, cli commands and reads the cofiguration file.
Definition at line 2471 of file chan_agent.c.
References action_agent_logoff(), action_agents(), agent_function, agent_tech, agentmonitoroutgoing_exec(), agents_data_providers, ARRAY_LEN, ast_channel_register(), ast_cli_register_multiple(), ast_custom_function_register, ast_data_register_multiple, ast_log(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, cli_agents, EVENT_FLAG_AGENT, LOG_ERROR, login_exec(), and read_agent_config().
02472 { 02473 /* Make sure we can register our agent channel type */ 02474 if (ast_channel_register(&agent_tech)) { 02475 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n"); 02476 return AST_MODULE_LOAD_FAILURE; 02477 } 02478 /* Read in the config */ 02479 if (!read_agent_config(0)) 02480 return AST_MODULE_LOAD_DECLINE; 02481 /* Dialplan applications */ 02482 ast_register_application_xml(app, login_exec); 02483 ast_register_application_xml(app3, agentmonitoroutgoing_exec); 02484 02485 /* data tree */ 02486 ast_data_register_multiple(agents_data_providers, ARRAY_LEN(agents_data_providers)); 02487 02488 /* Manager commands */ 02489 ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents); 02490 ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff); 02491 02492 /* CLI Commands */ 02493 ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents)); 02494 02495 /* Dialplan Functions */ 02496 ast_custom_function_register(&agent_function); 02497 02498 return AST_MODULE_LOAD_SUCCESS; 02499 }
static int login_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Log in agent application.
Called by the AgentLogin application (from the dial plan).
chan | ||
data |
Definition at line 1869 of file chan_agent.c.
References ast_channel::_state, agent_pvt::acceptdtmf, agent_pvt::ackcall, agent_pvt::acknowledged, agent_pvt::agent, agent_ack_sleep(), agent_cont_sleep(), AGENT_FLAG_ACCEPTDTMF, AGENT_FLAG_ACKCALL, AGENT_FLAG_AUTOLOGOFF, AGENT_FLAG_ENDDTMF, AGENT_FLAG_WRAPUPTIME, agent_pvt_destroy(), agent_pvt::app_complete_cond, agent_pvt::app_lock_flag, args, ast_answer(), AST_APP_ARG, ast_app_getdata(), ast_best_codec(), ast_channel_lock, ast_channel_unlock, ast_check_hangup(), ast_cond_signal, ast_cond_wait, AST_CONTROL_HOLD, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_getformatname(), ast_indicate_data(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_AGENT, AST_MAX_FILENAME_LEN, ast_mutex_lock, ast_mutex_unlock, ast_queue_log(), ast_safe_sleep(), ast_safe_sleep_conditional(), ast_set_flag, ast_set_read_format(), ast_set_write_format(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_true(), ast_tv(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitstream(), agent_pvt::autologoff, ast_channel::cdr, agent_pvt::chan, ast_cdr::channel, check_availability(), check_beep(), agent_pvt::dead, agent_pvt::deferlogoff, agent_pvt::enddtmf, EVENT_FLAG_AGENT, agent_pvt::lastdisc, agent_pvt::lock, LOG_NOTICE, LOG_WARNING, agent_pvt::login_wait_cond, agent_pvt::loginstart, manager_event, agent_pvt::moh, ast_channel::nativeformats, agent_pvt::owner, parse(), pass, agent_pvt::password, pbx_builtin_getvar_helper(), agent_pvt::pending, ast_channel::readformat, S_OR, update_cdr, agent_pvt::wrapuptime, and ast_channel::writeformat.
Referenced by load_module().
01870 { 01871 int res=0; 01872 int tries = 0; 01873 int max_login_tries = maxlogintries; 01874 struct agent_pvt *p; 01875 char user[AST_MAX_AGENT]; 01876 char pass[AST_MAX_AGENT]; 01877 char xpass[AST_MAX_AGENT]; 01878 char *errmsg; 01879 char *parse; 01880 AST_DECLARE_APP_ARGS(args, 01881 AST_APP_ARG(agent_id); 01882 AST_APP_ARG(options); 01883 AST_APP_ARG(extension); 01884 ); 01885 const char *tmpoptions = NULL; 01886 int play_announcement = 1; 01887 char agent_goodbye[AST_MAX_FILENAME_LEN]; 01888 int update_cdr = updatecdr; 01889 01890 user[0] = '\0'; 01891 xpass[0] = '\0'; 01892 01893 parse = ast_strdupa(data); 01894 01895 AST_STANDARD_APP_ARGS(args, parse); 01896 01897 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye)); 01898 01899 ast_channel_lock(chan); 01900 /* Set Channel Specific Login Overrides */ 01901 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) { 01902 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES")); 01903 if (max_login_tries < 0) 01904 max_login_tries = 0; 01905 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"); 01906 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name); 01907 } 01908 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) { 01909 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) 01910 update_cdr = 1; 01911 else 01912 update_cdr = 0; 01913 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"); 01914 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name); 01915 } 01916 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) { 01917 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE")); 01918 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"); 01919 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name); 01920 } 01921 ast_channel_unlock(chan); 01922 /* End Channel Specific Login Overrides */ 01923 01924 if (!ast_strlen_zero(args.options)) { 01925 if (strchr(args.options, 's')) { 01926 play_announcement = 0; 01927 } 01928 } 01929 01930 if (chan->_state != AST_STATE_UP) 01931 res = ast_answer(chan); 01932 if (!res) { 01933 if (!ast_strlen_zero(args.agent_id)) 01934 ast_copy_string(user, args.agent_id, AST_MAX_AGENT); 01935 else 01936 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); 01937 } 01938 while (!res && (max_login_tries==0 || tries < max_login_tries)) { 01939 tries++; 01940 /* Check for password */ 01941 AST_LIST_LOCK(&agents); 01942 AST_LIST_TRAVERSE(&agents, p, list) { 01943 if (!strcmp(p->agent, user) && !p->pending) 01944 ast_copy_string(xpass, p->password, sizeof(xpass)); 01945 } 01946 AST_LIST_UNLOCK(&agents); 01947 if (!res) { 01948 if (!ast_strlen_zero(xpass)) 01949 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0); 01950 else 01951 pass[0] = '\0'; 01952 } 01953 errmsg = "agent-incorrect"; 01954 01955 #if 0 01956 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass); 01957 #endif 01958 01959 /* Check again for accuracy */ 01960 AST_LIST_LOCK(&agents); 01961 AST_LIST_TRAVERSE(&agents, p, list) { 01962 int unlock_channel = 1; 01963 01964 ast_channel_lock(chan); 01965 ast_mutex_lock(&p->lock); 01966 if (!strcmp(p->agent, user) && 01967 !strcmp(p->password, pass) && !p->pending) { 01968 01969 /* Set Channel Specific Agent Overrides */ 01970 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { 01971 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { 01972 p->ackcall = 1; 01973 } else { 01974 p->ackcall = 0; 01975 } 01976 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); 01977 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent); 01978 ast_set_flag(p, AGENT_FLAG_ACKCALL); 01979 } else { 01980 p->ackcall = ackcall; 01981 } 01982 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) { 01983 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF")); 01984 if (p->autologoff < 0) 01985 p->autologoff = 0; 01986 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"); 01987 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent); 01988 ast_set_flag(p, AGENT_FLAG_AUTOLOGOFF); 01989 } else { 01990 p->autologoff = autologoff; 01991 } 01992 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) { 01993 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME")); 01994 if (p->wrapuptime < 0) 01995 p->wrapuptime = 0; 01996 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"); 01997 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent); 01998 ast_set_flag(p, AGENT_FLAG_WRAPUPTIME); 01999 } else { 02000 p->wrapuptime = wrapuptime; 02001 } 02002 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF"); 02003 if (!ast_strlen_zero(tmpoptions)) { 02004 p->acceptdtmf = *tmpoptions; 02005 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent); 02006 ast_set_flag(p, AGENT_FLAG_ACCEPTDTMF); 02007 } 02008 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF"); 02009 if (!ast_strlen_zero(tmpoptions)) { 02010 p->enddtmf = *tmpoptions; 02011 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent); 02012 ast_set_flag(p, AGENT_FLAG_ENDDTMF); 02013 } 02014 ast_channel_unlock(chan); 02015 unlock_channel = 0; 02016 /* End Channel Specific Agent Overrides */ 02017 02018 if (!p->chan) { 02019 /* Ensure nobody else can be this agent until we're done. */ 02020 p->chan = chan; 02021 02022 p->acknowledged = 0; 02023 02024 if (!res) { 02025 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats)); 02026 if (res) 02027 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats))); 02028 } 02029 if (!res) { 02030 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats)); 02031 if (res) 02032 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats))); 02033 } 02034 if (!res && play_announcement == 1) { 02035 ast_mutex_unlock(&p->lock); 02036 AST_LIST_UNLOCK(&agents); 02037 res = ast_streamfile(chan, "agent-loginok", chan->language); 02038 if (!res) { 02039 ast_waitstream(chan, ""); 02040 } 02041 AST_LIST_LOCK(&agents); 02042 ast_mutex_lock(&p->lock); 02043 } 02044 02045 if (!res) { 02046 long logintime; 02047 char agent[AST_MAX_AGENT]; 02048 02049 snprintf(agent, sizeof(agent), "Agent/%s", p->agent); 02050 02051 /* Login this channel and wait for it to go away */ 02052 ast_indicate_data(chan, AST_CONTROL_HOLD, 02053 S_OR(p->moh, NULL), 02054 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0); 02055 02056 /* Must be done after starting HOLD. */ 02057 p->lastdisc = ast_tvnow(); 02058 time(&p->loginstart); 02059 02060 manager_event(EVENT_FLAG_AGENT, "Agentlogin", 02061 "Agent: %s\r\n" 02062 "Channel: %s\r\n" 02063 "Uniqueid: %s\r\n", 02064 p->agent, chan->name, chan->uniqueid); 02065 if (update_cdr && chan->cdr) 02066 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "%s", agent); 02067 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name); 02068 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent, 02069 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat)); 02070 02071 ast_mutex_unlock(&p->lock); 02072 AST_LIST_UNLOCK(&agents); 02073 02074 while (res >= 0) { 02075 ast_mutex_lock(&p->lock); 02076 if (p->deferlogoff) { 02077 p->deferlogoff = 0; 02078 ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT); 02079 ast_mutex_unlock(&p->lock); 02080 break; 02081 } 02082 ast_mutex_unlock(&p->lock); 02083 02084 AST_LIST_LOCK(&agents); 02085 ast_mutex_lock(&p->lock); 02086 if (p->lastdisc.tv_sec) { 02087 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { 02088 ast_debug(1, "Wrapup time for %s expired!\n", agent); 02089 p->lastdisc = ast_tv(0, 0); 02090 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent); 02091 if (p->ackcall) { 02092 check_beep(p, 0); 02093 } else { 02094 check_availability(p, 0); 02095 } 02096 } 02097 } 02098 ast_mutex_unlock(&p->lock); 02099 AST_LIST_UNLOCK(&agents); 02100 02101 /* Synchronize channel ownership between call to agent and itself. */ 02102 ast_mutex_lock(&p->lock); 02103 if (p->app_lock_flag) { 02104 ast_cond_signal(&p->login_wait_cond); 02105 ast_cond_wait(&p->app_complete_cond, &p->lock); 02106 if (ast_check_hangup(chan)) { 02107 /* Agent hungup */ 02108 ast_mutex_unlock(&p->lock); 02109 break; 02110 } 02111 } 02112 ast_mutex_unlock(&p->lock); 02113 02114 if (p->ackcall) { 02115 res = agent_ack_sleep(p); 02116 if (res == 1) { 02117 AST_LIST_LOCK(&agents); 02118 ast_mutex_lock(&p->lock); 02119 check_availability(p, 0); 02120 ast_mutex_unlock(&p->lock); 02121 AST_LIST_UNLOCK(&agents); 02122 } 02123 } else { 02124 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p ); 02125 } 02126 } 02127 ast_mutex_lock(&p->lock); 02128 02129 /* Logoff this channel */ 02130 p->chan = NULL; 02131 logintime = time(NULL) - p->loginstart; 02132 p->loginstart = 0; 02133 02134 /* Synchronize channel ownership between call to agent and itself. */ 02135 if (p->app_lock_flag) { 02136 ast_cond_signal(&p->login_wait_cond); 02137 ast_cond_wait(&p->app_complete_cond, &p->lock); 02138 } 02139 02140 if (p->owner) { 02141 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); 02142 } 02143 02144 p->acknowledged = 0; 02145 ast_mutex_unlock(&p->lock); 02146 02147 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s", agent); 02148 manager_event(EVENT_FLAG_AGENT, "Agentlogoff", 02149 "Agent: %s\r\n" 02150 "Logintime: %ld\r\n" 02151 "Uniqueid: %s\r\n", 02152 p->agent, logintime, chan->uniqueid); 02153 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime); 02154 ast_verb(2, "Agent '%s' logged out\n", p->agent); 02155 02156 /* If there is no owner, go ahead and kill it now */ 02157 if (p->dead && !p->owner) { 02158 agent_pvt_destroy(p); 02159 } 02160 AST_LIST_LOCK(&agents); 02161 } else { 02162 /* Agent hung up before could be logged in. */ 02163 p->chan = NULL; 02164 02165 ast_mutex_unlock(&p->lock); 02166 } 02167 res = -1; 02168 } else { 02169 ast_mutex_unlock(&p->lock); 02170 errmsg = "agent-alreadyon"; 02171 } 02172 break; 02173 } 02174 ast_mutex_unlock(&p->lock); 02175 if (unlock_channel) { 02176 ast_channel_unlock(chan); 02177 } 02178 } 02179 AST_LIST_UNLOCK(&agents); 02180 02181 if (!res && (max_login_tries==0 || tries < max_login_tries)) 02182 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); 02183 } 02184 02185 if (!res) 02186 res = ast_safe_sleep(chan, 500); 02187 02188 return -1; 02189 }
static force_inline int powerof | ( | unsigned int | d | ) | [static] |
Definition at line 1474 of file chan_agent.c.
Referenced by agents_show().
static int read_agent_config | ( | int | reload | ) | [static] |
Read configuration data. The file named agents.conf.
Definition at line 1112 of file chan_agent.c.
References add_agent(), agent_pvt_destroy(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_get_group(), AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), agent_pvt::chan, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, agent_pvt::dead, LOG_ERROR, LOG_NOTICE, ast_variable::name, ast_variable::next, agent_pvt::owner, secret, and ast_variable::value.
Referenced by load_module(), and reload().
01113 { 01114 struct ast_config *cfg; 01115 struct ast_config *ucfg; 01116 struct ast_variable *v; 01117 struct agent_pvt *p; 01118 const char *catname; 01119 const char *hasagent; 01120 int genhasagent; 01121 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 01122 01123 group = 0; 01124 autologoff = 0; 01125 wrapuptime = 0; 01126 ackcall = 0; 01127 endcall = 1; 01128 cfg = ast_config_load(config, config_flags); 01129 if (!cfg) { 01130 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n"); 01131 return 0; 01132 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 01133 return -1; 01134 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 01135 ast_log(LOG_ERROR, "%s contains a parsing error. Aborting\n", config); 01136 return 0; 01137 } 01138 if ((ucfg = ast_config_load("users.conf", config_flags))) { 01139 if (ucfg == CONFIG_STATUS_FILEUNCHANGED) { 01140 ucfg = NULL; 01141 } else if (ucfg == CONFIG_STATUS_FILEINVALID) { 01142 ast_log(LOG_ERROR, "users.conf contains a parsing error. Aborting\n"); 01143 return 0; 01144 } 01145 } 01146 01147 AST_LIST_LOCK(&agents); 01148 AST_LIST_TRAVERSE(&agents, p, list) { 01149 p->dead = 1; 01150 } 01151 strcpy(moh, "default"); 01152 /* set the default recording values */ 01153 recordagentcalls = 0; 01154 strcpy(recordformat, "wav"); 01155 strcpy(recordformatext, "wav"); 01156 urlprefix[0] = '\0'; 01157 savecallsin[0] = '\0'; 01158 01159 /* Read in the [agents] section */ 01160 v = ast_variable_browse(cfg, "agents"); 01161 while(v) { 01162 /* Create the interface list */ 01163 if (!strcasecmp(v->name, "agent")) { 01164 add_agent(v->value, 0); 01165 } else if (!strcasecmp(v->name, "group")) { 01166 group = ast_get_group(v->value); 01167 } else if (!strcasecmp(v->name, "autologoff")) { 01168 autologoff = atoi(v->value); 01169 if (autologoff < 0) 01170 autologoff = 0; 01171 } else if (!strcasecmp(v->name, "ackcall")) { 01172 if (ast_true(v->value) || !strcasecmp(v->value, "always")) { 01173 ackcall = 1; 01174 } 01175 } else if (!strcasecmp(v->name, "endcall")) { 01176 endcall = ast_true(v->value); 01177 } else if (!strcasecmp(v->name, "acceptdtmf")) { 01178 acceptdtmf = *(v->value); 01179 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf); 01180 } else if (!strcasecmp(v->name, "enddtmf")) { 01181 enddtmf = *(v->value); 01182 } else if (!strcasecmp(v->name, "wrapuptime")) { 01183 wrapuptime = atoi(v->value); 01184 if (wrapuptime < 0) 01185 wrapuptime = 0; 01186 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) { 01187 maxlogintries = atoi(v->value); 01188 if (maxlogintries < 0) 01189 maxlogintries = 0; 01190 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) { 01191 strcpy(agentgoodbye,v->value); 01192 } else if (!strcasecmp(v->name, "musiconhold")) { 01193 ast_copy_string(moh, v->value, sizeof(moh)); 01194 } else if (!strcasecmp(v->name, "updatecdr")) { 01195 if (ast_true(v->value)) 01196 updatecdr = 1; 01197 else 01198 updatecdr = 0; 01199 } else if (!strcasecmp(v->name, "autologoffunavail")) { 01200 if (ast_true(v->value)) 01201 autologoffunavail = 1; 01202 else 01203 autologoffunavail = 0; 01204 } else if (!strcasecmp(v->name, "recordagentcalls")) { 01205 recordagentcalls = ast_true(v->value); 01206 } else if (!strcasecmp(v->name, "recordformat")) { 01207 ast_copy_string(recordformat, v->value, sizeof(recordformat)); 01208 if (!strcasecmp(v->value, "wav49")) 01209 strcpy(recordformatext, "WAV"); 01210 else 01211 ast_copy_string(recordformatext, v->value, sizeof(recordformatext)); 01212 } else if (!strcasecmp(v->name, "urlprefix")) { 01213 ast_copy_string(urlprefix, v->value, sizeof(urlprefix)); 01214 if (urlprefix[strlen(urlprefix) - 1] != '/') 01215 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1); 01216 } else if (!strcasecmp(v->name, "savecallsin")) { 01217 if (v->value[0] == '/') 01218 ast_copy_string(savecallsin, v->value, sizeof(savecallsin)); 01219 else 01220 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value); 01221 if (savecallsin[strlen(savecallsin) - 1] != '/') 01222 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1); 01223 } else if (!strcasecmp(v->name, "custom_beep")) { 01224 ast_copy_string(beep, v->value, sizeof(beep)); 01225 } 01226 v = v->next; 01227 } 01228 if (ucfg) { 01229 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent")); 01230 catname = ast_category_browse(ucfg, NULL); 01231 while(catname) { 01232 if (strcasecmp(catname, "general")) { 01233 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent"); 01234 if (ast_true(hasagent) || (!hasagent && genhasagent)) { 01235 char tmp[256]; 01236 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname"); 01237 const char *secret = ast_variable_retrieve(ucfg, catname, "secret"); 01238 if (!fullname) 01239 fullname = ""; 01240 if (!secret) 01241 secret = ""; 01242 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname); 01243 add_agent(tmp, 0); 01244 } 01245 } 01246 catname = ast_category_browse(ucfg, catname); 01247 } 01248 ast_config_destroy(ucfg); 01249 } 01250 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) { 01251 if (p->dead) { 01252 AST_LIST_REMOVE_CURRENT(list); 01253 /* Destroy if appropriate */ 01254 if (!p->owner) { 01255 if (!p->chan) { 01256 agent_pvt_destroy(p); 01257 } else { 01258 /* Cause them to hang up */ 01259 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); 01260 } 01261 } 01262 } 01263 } 01264 AST_LIST_TRAVERSE_SAFE_END; 01265 AST_LIST_UNLOCK(&agents); 01266 ast_config_destroy(cfg); 01267 return 1; 01268 }
static int reload | ( | void | ) | [static] |
Definition at line 2501 of file chan_agent.c.
References read_agent_config().
02502 { 02503 return read_agent_config(1); 02504 }
static int unload_module | ( | void | ) | [static] |
Definition at line 2506 of file chan_agent.c.
References agent_function, agent_tech, ARRAY_LEN, ast_channel_unregister(), ast_cli_unregister_multiple(), ast_custom_function_unregister(), ast_data_unregister, ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_manager_unregister(), ast_softhangup(), AST_SOFTHANGUP_APPUNLOAD, ast_unregister_application(), cli_agents, and agent_pvt::owner.
02507 { 02508 struct agent_pvt *p; 02509 /* First, take us out of the channel loop */ 02510 ast_channel_unregister(&agent_tech); 02511 /* Unregister dialplan functions */ 02512 ast_custom_function_unregister(&agent_function); 02513 /* Unregister CLI commands */ 02514 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents)); 02515 /* Unregister dialplan applications */ 02516 ast_unregister_application(app); 02517 ast_unregister_application(app3); 02518 /* Unregister manager command */ 02519 ast_manager_unregister("Agents"); 02520 ast_manager_unregister("AgentLogoff"); 02521 /* Unregister the data tree */ 02522 ast_data_unregister(NULL); 02523 /* Unregister channel */ 02524 AST_LIST_LOCK(&agents); 02525 /* Hangup all interfaces if they have an owner */ 02526 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) { 02527 if (p->owner) 02528 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); 02529 ast_free(p); 02530 } 02531 AST_LIST_UNLOCK(&agents); 02532 return 0; 02533 }
char acceptdtmf = DEFAULT_ACCEPTDTMF [static] |
Definition at line 227 of file chan_agent.c.
Referenced by play_record_review().
int ackcall [static] |
Definition at line 224 of file chan_agent.c.
struct ast_custom_function agent_function [static] |
{ .name = "AGENT", .read = function_agent, }
Definition at line 2371 of file chan_agent.c.
Referenced by load_module(), and unload_module().
const char agent_logoff_usage[] [static] |
"Usage: agent logoff <channel> [soft]\n" " Sets an agent as no longer logged in.\n" " If 'soft' is specified, do not hangup existing calls.\n"
Definition at line 1848 of file chan_agent.c.
struct ast_channel_tech agent_tech [static] |
Channel interface description for PBX integration.
Definition at line 349 of file chan_agent.c.
Referenced by agent_new(), load_module(), and unload_module().
char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye" [static] |
Definition at line 231 of file chan_agent.c.
struct ast_data_handler agents_data_provider [static] |
{ .version = AST_DATA_HANDLER_VERSION, .get = agents_data_provider_get }
Definition at line 2455 of file chan_agent.c.
struct ast_data_entry agents_data_providers[] [static] |
{ AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider), }
Definition at line 2460 of file chan_agent.c.
Referenced by load_module().
const char app[] = "AgentLogin" [static] |
Definition at line 206 of file chan_agent.c.
const char app3[] = "AgentMonitorOutgoing" [static] |
Definition at line 207 of file chan_agent.c.
int autologoff [static] |
Definition at line 222 of file chan_agent.c.
int autologoffunavail = 0 [static] |
Definition at line 226 of file chan_agent.c.
char beep[AST_MAX_BUF] = "beep" [static] |
Definition at line 239 of file chan_agent.c.
struct ast_cli_entry cli_agents[] [static] |
{ AST_CLI_DEFINE(agents_show, "Show status of agents"), AST_CLI_DEFINE(agents_show_online, "Show all online agents"), AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"), }
Definition at line 1853 of file chan_agent.c.
Referenced by load_module(), and unload_module().
const char config[] = "agents.conf" [static] |
Definition at line 204 of file chan_agent.c.
int endcall [static] |
Definition at line 225 of file chan_agent.c.
char enddtmf = DEFAULT_ENDDTMF [static] |
Definition at line 228 of file chan_agent.c.
ast_group_t group [static] |
Definition at line 221 of file chan_agent.c.
int maxlogintries = 3 [static] |
Definition at line 230 of file chan_agent.c.
char moh[80] = "default" [static] |
Definition at line 209 of file chan_agent.c.
Referenced by _get_mohbyname(), dial_exec_full(), moh_generate(), moh_release(), mohalloc(), and monmp3thread().
const char pa_family[] = "Agents" [static] |
Persistent Agents astdb family
Definition at line 215 of file chan_agent.c.
int recordagentcalls = 0 [static] |
Definition at line 233 of file chan_agent.c.
char recordformat[AST_MAX_BUF] = "" [static] |
Definition at line 234 of file chan_agent.c.
char recordformatext[AST_MAX_BUF] = "" [static] |
Definition at line 235 of file chan_agent.c.
char savecallsin[AST_MAX_BUF] = "" [static] |
Definition at line 237 of file chan_agent.c.
const char tdesc[] = "Call Agent Proxy Channel" [static] |
Definition at line 203 of file chan_agent.c.
int updatecdr = 0 [static] |
Definition at line 238 of file chan_agent.c.
char urlprefix[AST_MAX_BUF] = "" [static] |
Definition at line 236 of file chan_agent.c.
Referenced by start_monitor_exec().
int wrapuptime [static] |
Definition at line 223 of file chan_agent.c.