Add BUGBUG note for ASTERISK-22009

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393631 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett 2013-07-03 23:55:53 +00:00
parent c841c34aae
commit b4e9a3fc2f
12 changed files with 2876 additions and 2732 deletions

28
CHANGES
View File

@ -14,10 +14,19 @@
Applications Applications
------------------ ------------------
AgentLogin
------------------
* The application no longer does agent authentication. The dialplan needs to
perform this function before running AgentLogin. If the agent is already
logged in, dialplan will continue with the AGENT_STATUS channel variable
set to ALREADY_LOGGED_IN.
AgentMonitorOutgoing AgentMonitorOutgoing
------------------ ------------------
* The 'c' option has been removed. It is not possible to modify the name of a * The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR. channel involved in a CDR.
* Application removed. It was a holdover from when AgentCallbackLogin was
removed.
ForkCDR ForkCDR
------------------ ------------------
@ -244,8 +253,8 @@ AMI (Asterisk Manager Interface)
of "CallerID" and "ConnectedID" to avoid confusion with similarly named of "CallerID" and "ConnectedID" to avoid confusion with similarly named
parameters in the channel snapshot. parameters in the channel snapshot.
* The "Agentlogin" and "Agentlogoff" events have been renamed "AgentLogin" and * The AMI events "Agentlogin" and "Agentlogoff" have been renamed
"AgentLogoff" respectively. "AgentLogin" and "AgentLogoff" respectively.
* The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been * The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been
renamed "DAHDIChannel" since it does not convey an Asterisk channel name. renamed "DAHDIChannel" since it does not convey an Asterisk channel name.
@ -423,6 +432,21 @@ chan_agent
and pretending otherwise helps no one. and pretending otherwise helps no one.
* The AGENTUPDATECDR channel variable has also been removed, for the same * The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option. reason as the updatecdr option.
* The driver is no longer a Data retrieval API data provider for the
AMI DataGet action.
* The endcall and enddtmf configuration options are removed. Use the
dialplan function CHANNEL(dtmf-features) to set DTMF features on the agent
channel before calling AgentLogin.
* chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
applications. Agents are connected with callers using the new AgentRequest
dialplan application. The Agents:<agent-id> device state is available to
monitor the status of an agent. See agents.conf.sample for valid
configuration options.
chan_bridge
------------------
* chan_bridge is removed and its functionality is incorporated into ConfBridge
itself.
chan_local chan_local
------------------ ------------------

View File

@ -24,6 +24,8 @@
AgentMonitorOutgoing AgentMonitorOutgoing
- The 'c' option has been removed. It is not possible to modify the name of a - The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR. channel involved in a CDR.
- Application removed. It was a holdover from when AgentCallbackLogin was
removed.
NoCDR: NoCDR:
- This application is deprecated. Please use the CDR_PROP function instead. - This application is deprecated. Please use the CDR_PROP function instead.
@ -124,6 +126,15 @@ chan_agent:
and pretending otherwise helps no one. and pretending otherwise helps no one.
- The AGENTUPDATECDR channel variable has also been removed, for the same - The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option. reason as the updatecdr option.
- chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
applications. Agents are connected with callers using the new AgentRequest
dialplan application. The Agents:<agent-id> device state is available to
monitor the status of an agent. See agents.conf.sample for valid
configuration options.
chan_bridge
- chan_bridge is removed and its functionality is incorporated into ConfBridge
itself.
chan_dahdi: chan_dahdi:
- Analog port dialing and deferred DTMF dialing for PRI now distinguishes - Analog port dialing and deferred DTMF dialing for PRI now distinguishes

2486
apps/app_agent_pool.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,102 +1,68 @@
; ;
; Agent configuration ; Agent pool configuration
; ;
[general] [general]
; The general section of this config is not currently used, but reserved
; for future use.
[agents] ;[agent-id]
; Define ackcall to require the agent to give a DTMF acknowledgement
; when the agent receives a call.
; The channel variable AGENTACKCALL overrides on agent login.
; Default is "no".
;ackcall=no
; ;
; Define maxlogintries to allow agent to try max logins before ; Set what DTMF key sequence the agent should use to acknowledge a call.
; failed. ; The channel variable AGENTACCEPTDTMF overrides on agent login.
; default to 3 ; Default is "#".
; ;acceptdtmf=##
;maxlogintries=5
;
;
; Define autologoff times if appropriate. This is how long
; the phone has to ring with no answer before the agent is
; automatically logged off (in seconds)
; ;
; Set how many seconds a call for the agent has to wait for the agent to
; acknowledge the call before the agent is automatically logged off. If
; set to zero then the call will wait forever for the agent to acknowledge.
; The channel variable AGENTAUTOLOGOFF overrides on agent login.
; Default is 0.
;autologoff=15 ;autologoff=15
; ;
; Define autologoffunavail to have agents automatically logged ; Set the minimum amount of time after disconnecting a call before
; out when the extension that they are at returns a CHANUNAVAIL ; the agent can receive a new call in milliseconds.
; status when a call is attempted to be sent there. ; The channel variable AGENTWRAPUPTIME overrides on agent login.
; Default is "no". ; Default is 0.
;
;autologoffunavail=yes
;
; Define ackcall to require a DTMF acknowledgement when
; a logged-in agent receives a call. Default is "no".
; Use the acceptdtmf option to configure what DTMF key
; press should be used to acknowledge the call. The
; default is '#'.
;
;ackcall=no
;acceptdtmf=#
;
; Define endcall to allow an agent to hangup a call with a
; DTMF keypress. Default is "yes". Use the enddtmf option to
; configure which DTMF key will end a call. The default is
; '*'.
;
;endcall=yes
;enddtmf=*
;
; Define wrapuptime. This is the minimum amount of time when
; after disconnecting before the caller can receive a new call
; note this is in milliseconds.
;
;wrapuptime=5000 ;wrapuptime=5000
; ;
; Define the default musiconhold for agents ; Set the musiconhold class for the agent.
; musiconhold => music_class ; Default is "default".
;musiconhold=default
; ;
;musiconhold => default ; Enable recording calls the agent takes automatically by invoking the
; ; DTMF automixmon feature when the agent connects to a caller.
; Define the default good bye sound file for agents ; See features.conf.sample for information about the automixmon feature.
; default to vm-goodbye ; Default is "no".
;
;goodbye => goodbye_file
;
; Define updatecdr. This is whether or not to change the source
; channel in the CDR record for this call to agent/agent_id so
; that we know which agent generates the call
;
;updatecdr=no
;
; Group memberships for agents (may change in mid-file)
;
;group=3
;group=1,2
;group=
;
; --------------------------------------------------
; This section is devoted to recording agent's calls
; The keywords are global to the chan_agent channel driver
;
; Enable recording calls addressed to agents. It's turned off by default.
;recordagentcalls=yes ;recordagentcalls=yes
; ;
; The format to be used to record the calls: wav, gsm, wav49. ; The sound file played to alert the agent when a call is present.
; By default its "wav". ; Default is "beep".
;recordformat=gsm
;
; The text to be added to the name of the recording. Allows forming a url link.
;urlprefix=http://localhost/calls/
;
; The optional directory to save the conversations in. The default is
; /var/spool/asterisk/monitor
;savecallsin=/var/calls
;
; An optional custom beep sound file to play to always-connected agents.
;custom_beep=beep ;custom_beep=beep
; ;
; A friendly name for the agent used in log messages.
; Default is "".
;fullname=Mark Spencer
;
; -------------------------------------------------- ; --------------------------------------------------
; ;
; This section contains the agent definitions, in the form: ; This section contains example agent definitions:
; ;
; agent => agentid,agentpassword,name ; Define a template called my-agents:
;[my-agents](!)
;autologoff=15
;ackcall=yes
;acceptdtmf=##
; ;
;agent => 1001,4321,Mark Spencer ; Define agent 1001 using the my-agents template:
;agent => 1002,4321,Will Meadows ;[1001](my-agents)
;fullname=Mark Spencer
;
; Define agent 1002 using the my-agents template:
;[1002](my-agents)
;fullname=Will Meadows

View File

@ -543,18 +543,7 @@ monitor-type = MixMonitor
;member => DAHDI/1 ;member => DAHDI/1
;member => DAHDI/2,10 ;member => DAHDI/2,10
;member => DAHDI/3,10,Bob Johnson ;member => DAHDI/3,10,Bob Johnson
;member => Agent/1001 ;member => Local/1001@agents,0,May Flowers,Agent:1001
;member => Agent/1002 ;member => Local/1002@agents,0,John Doe,Agent:1002
;member => Local/1000@default,0,John Smith,SIP/1000 ;member => Local/1000@default,0,John Smith,SIP/1000
;member => Local/2000@default,0,Lorem Ipsum,SIP/2000,no ;member => Local/2000@default,0,Lorem Ipsum,SIP/2000,no
;
; Note that using agent groups is probably not what you want. Strategies do
; not propagate down to the Agent system so if you want round robin, least
; recent, etc, you should list all the agents in this file individually and not
; use agent groups.
;
;member => Agent/@1 ; Any agent in group 1
;member => Agent/:1,1 ; Any agent in group 1, wait for first
; available, but consider with penalty

View File

@ -1647,7 +1647,7 @@ void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t b
/*! Reason the the after bridge callback will not be called. */ /*! Reason the the after bridge callback will not be called. */
enum ast_after_bridge_cb_reason { enum ast_after_bridge_cb_reason {
/*! The datastore is being destroyed. Likely due to hangup. */ /*! The datastore is being destroyed. Likely due to hangup. (Enum value must be zero.) */
AST_AFTER_BRIDGE_CB_REASON_DESTROY, AST_AFTER_BRIDGE_CB_REASON_DESTROY,
/*! Something else replaced the callback with another. */ /*! Something else replaced the callback with another. */
AST_AFTER_BRIDGE_CB_REASON_REPLACED, AST_AFTER_BRIDGE_CB_REASON_REPLACED,
@ -1666,6 +1666,9 @@ enum ast_after_bridge_cb_reason {
* \param reason Reason callback is failing. * \param reason Reason callback is failing.
* \param data Extra data what setup the callback wanted to pass. * \param data Extra data what setup the callback wanted to pass.
* *
* \note Called when the channel leaves the bridging system or
* is destroyed.
*
* \return Nothing * \return Nothing
*/ */
typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data); typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
@ -1705,6 +1708,9 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
* *
* \note chan is locked by this function. * \note chan is locked by this function.
* *
* \note failed is called when the channel leaves the bridging
* system or is destroyed.
*
* \retval 0 on success. * \retval 0 on success.
* \retval -1 on error. * \retval -1 on error.
*/ */

View File

@ -461,7 +461,7 @@ enum aco_process_status {
/*! \brief Process a config info via the options registered with an aco_info /*! \brief Process a config info via the options registered with an aco_info
* *
* \param info The config_options_info to be used for handling the config * \param info The config_options_info to be used for handling the config
* \param reload Whether or not this is a reload * \param reload Non-zero if this is for a reload.
* *
* \retval ACO_PROCESS_OK Success * \retval ACO_PROCESS_OK Success
* \retval ACO_PROCESS_ERROR Failure * \retval ACO_PROCESS_ERROR Failure

View File

@ -421,6 +421,22 @@ struct stasis_message_type *ast_channel_monitor_start_type(void);
*/ */
struct stasis_message_type *ast_channel_monitor_stop_type(void); struct stasis_message_type *ast_channel_monitor_stop_type(void);
/*!
* \since 12.0.0
* \brief Message type for agent login on a channel
*
* \retval A stasis message type
*/
struct stasis_message_type *ast_channel_agent_login_type(void);
/*!
* \since 12.0.0
* \brief Message type for agent logoff on a channel
*
* \retval A stasis message type
*/
struct stasis_message_type *ast_channel_agent_logoff_type(void);
/*! /*!
* \since 12 * \since 12
* \brief Message type for starting music on hold on a channel * \brief Message type for starting music on hold on a channel

View File

@ -3145,15 +3145,70 @@ static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge
return bridge_channel; return bridge_channel;
} }
struct after_bridge_cb_ds { struct after_bridge_cb_node {
/*! Next list node. */
AST_LIST_ENTRY(after_bridge_cb_node) list;
/*! Desired callback function. */ /*! Desired callback function. */
ast_after_bridge_cb callback; ast_after_bridge_cb callback;
/*! After bridge callback will not be called and destroy any resources data may contain. */ /*! After bridge callback will not be called and destroy any resources data may contain. */
ast_after_bridge_cb_failed failed; ast_after_bridge_cb_failed failed;
/*! Extra data to pass to the callback. */ /*! Extra data to pass to the callback. */
void *data; void *data;
/*! Reason the after bridge callback failed. */
enum ast_after_bridge_cb_reason reason;
}; };
struct after_bridge_cb_ds {
/*! After bridge callbacks container. */
AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
};
/*!
* \internal
* \brief Indicate after bridge callback failed.
* \since 12.0.0
*
* \param node After bridge callback node.
*
* \return Nothing
*/
static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
{
if (node->failed) {
node->failed(node->reason, node->data);
node->failed = NULL;
}
}
/*!
* \internal
* \brief Run discarding any after bridge callbacks.
* \since 12.0.0
*
* \param after_bridge After bridge callback container process.
* \param reason Why are we doing this.
*
* \return Nothing
*/
static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_node *node;
for (;;) {
AST_LIST_LOCK(&after_bridge->callbacks);
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
if (!node) {
break;
}
if (!node->reason) {
node->reason = reason;
}
after_bridge_cb_failed(node);
ast_free(node);
}
}
/*! /*!
* \internal * \internal
* \brief Destroy the after bridge callback datastore. * \brief Destroy the after bridge callback datastore.
@ -3167,10 +3222,9 @@ static void after_bridge_cb_destroy(void *data)
{ {
struct after_bridge_cb_ds *after_bridge = data; struct after_bridge_cb_ds *after_bridge = data;
if (after_bridge->failed) { after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data);
after_bridge->failed = NULL; AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
}
ast_free(after_bridge); ast_free(after_bridge);
} }
@ -3187,7 +3241,6 @@ static void after_bridge_cb_destroy(void *data)
*/ */
static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
{ {
/* There can be only one. Discard any already on the new channel. */
ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE); ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
} }
@ -3199,47 +3252,67 @@ static const struct ast_datastore_info after_bridge_cb_info = {
/*! /*!
* \internal * \internal
* \brief Remove channel after the bridge callback and return it. * \brief Setup/create an after bridge callback datastore container.
* \since 12.0.0 * \since 12.0.0
* *
* \param chan Channel to remove after bridge callback. * \param chan Channel to setup/create the after bridge callback container on.
* *
* \retval datastore on success. * \retval after_bridge datastore container on success.
* \retval NULL on error or not found. * \retval NULL on error.
*/ */
static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan) static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
{ {
struct ast_datastore *datastore; struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge;
SCOPED_CHANNELLOCK(lock, chan);
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
if (datastore && ast_channel_datastore_remove(chan, datastore)) {
datastore = NULL;
}
ast_channel_unlock(chan);
return datastore;
}
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
{
struct ast_datastore *datastore;
datastore = after_bridge_cb_remove(chan);
if (datastore) { if (datastore) {
struct after_bridge_cb_ds *after_bridge = datastore->data; return datastore->data;
if (after_bridge && after_bridge->failed) {
after_bridge->failed(reason, after_bridge->data);
after_bridge->failed = NULL;
}
ast_datastore_free(datastore);
} }
/* Create a new datastore. */
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
if (!datastore) {
return NULL;
}
after_bridge = ast_calloc(1, sizeof(*after_bridge));
if (!after_bridge) {
ast_datastore_free(datastore);
return NULL;
}
AST_LIST_HEAD_INIT(&after_bridge->callbacks);
datastore->data = after_bridge;
ast_channel_datastore_add(chan, datastore);
return datastore->data;
} }
/*! /*!
* \internal * \internal
* \brief Run any after bridge callback if possible. * \brief Find an after bridge callback datastore container.
* \since 12.0.0
*
* \param chan Channel to find the after bridge callback container on.
*
* \retval after_bridge datastore container on success.
* \retval NULL on error.
*/
static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
{
struct ast_datastore *datastore;
SCOPED_CHANNELLOCK(lock, chan);
datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
if (!datastore) {
return NULL;
}
return datastore->data;
}
/*!
* \internal
* \brief Run any after bridge callback.
* \since 12.0.0 * \since 12.0.0
* *
* \param chan Channel to run after bridge callback. * \param chan Channel to run after bridge callback.
@ -3248,33 +3321,75 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
*/ */
static void after_bridge_callback_run(struct ast_channel *chan) static void after_bridge_callback_run(struct ast_channel *chan)
{ {
struct ast_datastore *datastore; struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *node;
after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return;
}
for (;;) {
AST_LIST_LOCK(&after_bridge->callbacks);
node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
if (!node) {
break;
}
if (node->reason) {
after_bridge_cb_failed(node);
} else {
node->failed = NULL;
node->callback(chan, node->data);
}
ast_free(node);
}
}
/*!
* \internal
* \brief Run discarding any after bridge callbacks.
* \since 12.0.0
*
* \param chan Channel to run after bridge callback.
*
* \return Nothing
*/
static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_ds *after_bridge; struct after_bridge_cb_ds *after_bridge;
if (ast_check_hangup(chan)) { after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return; return;
} }
/* Get after bridge goto datastore. */ after_bridge_cb_run_discard(after_bridge, reason);
datastore = after_bridge_cb_remove(chan); }
if (!datastore) {
void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
{
struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *node;
after_bridge = after_bridge_cb_find(chan);
if (!after_bridge) {
return; return;
} }
after_bridge = datastore->data; AST_LIST_LOCK(&after_bridge->callbacks);
if (after_bridge) { node = AST_LIST_LAST(&after_bridge->callbacks);
after_bridge->failed = NULL; if (node && !node->reason) {
after_bridge->callback(chan, after_bridge->data); node->reason = reason;
} }
AST_LIST_UNLOCK(&after_bridge->callbacks);
/* Discard after bridge callback datastore. */
ast_datastore_free(datastore);
} }
int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data) int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
{ {
struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge; struct after_bridge_cb_ds *after_bridge;
struct after_bridge_cb_node *new_node;
struct after_bridge_cb_node *last_node;
/* Sanity checks. */ /* Sanity checks. */
ast_assert(chan != NULL); ast_assert(chan != NULL);
@ -3282,29 +3397,28 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb
return -1; return -1;
} }
/* Create a new datastore. */ after_bridge = after_bridge_cb_setup(chan);
datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
if (!datastore) {
return -1;
}
after_bridge = ast_calloc(1, sizeof(*after_bridge));
if (!after_bridge) { if (!after_bridge) {
ast_datastore_free(datastore);
return -1; return -1;
} }
/* Initialize it. */ /* Create a new callback node. */
after_bridge->callback = callback; new_node = ast_calloc(1, sizeof(*new_node));
after_bridge->failed = failed; if (!new_node) {
after_bridge->data = data; return -1;
datastore->data = after_bridge; }
new_node->callback = callback;
/* Put it on the channel replacing any existing one. */ new_node->failed = failed;
ast_channel_lock(chan); new_node->data = data;
ast_after_bridge_callback_discard(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED);
ast_channel_datastore_add(chan, datastore);
ast_channel_unlock(chan);
/* Put it in the container disabling any previously active one. */
AST_LIST_LOCK(&after_bridge->callbacks);
last_node = AST_LIST_LAST(&after_bridge->callbacks);
if (last_node && !last_node->reason) {
last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
}
AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
AST_LIST_UNLOCK(&after_bridge->callbacks);
return 0; return 0;
} }
@ -3769,7 +3883,7 @@ static void *bridge_channel_depart_thread(void *data)
ast_bridge_features_destroy(bridge_channel->features); ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL; bridge_channel->features = NULL;
ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART); after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
ast_after_bridge_goto_discard(bridge_channel->chan); ast_after_bridge_goto_discard(bridge_channel->chan);
return NULL; return NULL;
@ -4993,6 +5107,23 @@ int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
return 0; return 0;
} }
int ast_bridge_features_do(enum ast_bridge_builtin_feature feature, struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
ast_bridge_hook_callback callback;
if (ARRAY_LEN(builtin_features_handlers) <= feature) {
return -1;
}
callback = builtin_features_handlers[feature];
if (!callback) {
return -1;
}
callback(bridge, bridge_channel, hook_pvt);
return 0;
}
int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback) int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback)
{ {
if (ARRAY_LEN(builtin_interval_handlers) <= interval if (ARRAY_LEN(builtin_interval_handlers) <= interval

View File

@ -598,6 +598,12 @@ enum aco_process_status aco_process_config(struct aco_info *info, int reload)
return ACO_PROCESS_ERROR; return ACO_PROCESS_ERROR;
} }
/*
* BUGBUG must fix config framework loading of multiple files.
*
* A reload with multiple files must reload all files if any
* file has been touched.
*/
while (res != ACO_PROCESS_ERROR && (file = info->files[x++])) { while (res != ACO_PROCESS_ERROR && (file = info->files[x++])) {
const char *filename = file->filename; const char *filename = file->filename;
try_alias: try_alias:

View File

@ -53,6 +53,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax> </syntax>
</managerEventInstance> </managerEventInstance>
</managerEvent> </managerEvent>
<managerEvent language="en_US" name="AgentLogin">
<managerEventInstance class="EVENT_FLAG_AGENT">
<synopsis>Raised when an Agent has logged in.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="Agent">
<para>The name of the agent.</para>
</parameter>
</syntax>
<see-also>
<ref type="application">AgentLogin</ref>
<ref type="managerEvent">Agentlogoff</ref>
</see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AgentLogoff">
<managerEventInstance class="EVENT_FLAG_AGENT">
<synopsis>Raised when an Agent has logged off.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentLogin']/managerEventInstance/syntax/parameter)" />
<parameter name="Logintime">
<para>The number of seconds the agent was logged in.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">AgentLogin</ref>
</see-also>
</managerEventInstance>
</managerEvent>
***/ ***/
#define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7 #define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7
@ -590,6 +619,44 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
ast_str_buffer(channel_event_string), variable, value); ast_str_buffer(channel_event_string), variable, value);
} }
static struct ast_manager_event_blob *agent_login_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
struct ast_channel_blob *obj = stasis_message_data(msg);
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
if (!channel_string) {
return NULL;
}
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogin",
"%s"
"Agent: %s\r\n",
ast_str_buffer(channel_string), agent);
}
static struct ast_manager_event_blob *agent_logoff_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
struct ast_channel_blob *obj = stasis_message_data(msg);
const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
long logintime = ast_json_integer_get(ast_json_object_get(obj->blob, "logintime"));
channel_string = ast_manager_build_channel_state_string(obj->snapshot);
if (!channel_string) {
return NULL;
}
return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogoff",
"%s"
"Agent: %s\r\n"
"Logintime: %ld\r\n",
ast_str_buffer(channel_string), agent, logintime);
}
void ast_publish_channel_state(struct ast_channel *chan) void ast_publish_channel_state(struct ast_channel *chan)
{ {
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
@ -790,6 +857,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type); STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_login_type,
.to_ami = agent_login_to_ami,
);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_logoff_type,
.to_ami = agent_logoff_to_ami,
);
/*! @} */ /*! @} */
@ -816,6 +889,8 @@ static void stasis_channels_cleanup(void)
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_login_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type);
} }
void ast_stasis_channels_init(void) void ast_stasis_channels_init(void)
@ -839,6 +914,8 @@ void ast_stasis_channels_init(void)
STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type); STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);
channel_topic_all = stasis_topic_create("ast_channel_topic_all"); channel_topic_all = stasis_topic_create("ast_channel_topic_all");
channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id); channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);