2009-06-26 15:28:53 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 - 2009, Digium, Inc.
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \file
|
|
|
|
*
|
|
|
|
* \brief Channel Event Logging API
|
|
|
|
*
|
|
|
|
* \author Steve Murphy <murf@digium.com>
|
|
|
|
* \author Russell Bryant <russell@digium.com>
|
|
|
|
*/
|
|
|
|
|
2012-10-18 14:17:40 +00:00
|
|
|
/*! \li \ref cel.c uses the configuration file \ref cel.conf
|
|
|
|
* \addtogroup configuration_file Configuration Files
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \page cel.conf cel.conf
|
|
|
|
* \verbinclude cel.conf.sample
|
|
|
|
*/
|
|
|
|
|
2012-06-15 16:20:16 +00:00
|
|
|
/*** MODULEINFO
|
|
|
|
<support_level>core</support_level>
|
|
|
|
***/
|
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
2018-02-17 03:11:42 +00:00
|
|
|
#include "asterisk/module.h"
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
#include "asterisk/channel.h"
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
#include "asterisk/cel.h"
|
|
|
|
#include "asterisk/logger.h"
|
|
|
|
#include "asterisk/linkedlists.h"
|
|
|
|
#include "asterisk/utils.h"
|
|
|
|
#include "asterisk/config.h"
|
2013-06-13 13:15:56 +00:00
|
|
|
#include "asterisk/config_options.h"
|
2009-06-26 15:28:53 +00:00
|
|
|
#include "asterisk/cli.h"
|
|
|
|
#include "asterisk/astobj2.h"
|
2013-06-13 13:15:56 +00:00
|
|
|
#include "asterisk/stasis_message_router.h"
|
|
|
|
#include "asterisk/stasis_channels.h"
|
2013-07-25 04:06:32 +00:00
|
|
|
#include "asterisk/stasis_bridges.h"
|
|
|
|
#include "asterisk/bridge.h"
|
2013-06-13 13:46:40 +00:00
|
|
|
#include "asterisk/parking.h"
|
2013-08-02 02:32:44 +00:00
|
|
|
#include "asterisk/pickup.h"
|
2013-07-20 13:25:05 +00:00
|
|
|
#include "asterisk/core_local.h"
|
2016-06-03 16:35:49 +00:00
|
|
|
#include "asterisk/taskprocessor.h"
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
/*** DOCUMENTATION
|
|
|
|
<configInfo name="cel" language="en_US">
|
|
|
|
<configFile name="cel.conf">
|
|
|
|
<configObject name="general">
|
|
|
|
<synopsis>Options that apply globally to Channel Event Logging (CEL)</synopsis>
|
|
|
|
<configOption name="enable">
|
|
|
|
<synopsis>Determines whether CEL is enabled</synopsis>
|
|
|
|
</configOption>
|
|
|
|
<configOption name="dateformat">
|
|
|
|
<synopsis>The format to be used for dates when logging</synopsis>
|
|
|
|
</configOption>
|
|
|
|
<configOption name="apps">
|
|
|
|
<synopsis>List of apps for CEL to track</synopsis>
|
|
|
|
<description><para>A case-insensitive, comma-separated list of applications
|
|
|
|
to track when one or both of APP_START and APP_END events are flagged for
|
|
|
|
tracking</para></description>
|
|
|
|
</configOption>
|
|
|
|
<configOption name="events">
|
|
|
|
<synopsis>List of events for CEL to track</synopsis>
|
|
|
|
<description><para>A case-sensitive, comma-separated list of event names
|
|
|
|
to track. These event names do not include the leading <literal>AST_CEL</literal>.
|
|
|
|
</para>
|
|
|
|
<enumlist>
|
|
|
|
<enum name="ALL">
|
|
|
|
<para>Special value which tracks all events.</para>
|
|
|
|
</enum>
|
|
|
|
<enum name="CHAN_START"/>
|
|
|
|
<enum name="CHAN_END"/>
|
|
|
|
<enum name="ANSWER"/>
|
|
|
|
<enum name="HANGUP"/>
|
|
|
|
<enum name="APP_START"/>
|
|
|
|
<enum name="APP_END"/>
|
|
|
|
<enum name="PARK_START"/>
|
|
|
|
<enum name="PARK_END"/>
|
|
|
|
<enum name="USER_DEFINED"/>
|
2013-08-22 17:13:16 +00:00
|
|
|
<enum name="BRIDGE_ENTER"/>
|
|
|
|
<enum name="BRIDGE_EXIT"/>
|
2013-06-13 13:15:56 +00:00
|
|
|
<enum name="BLINDTRANSFER"/>
|
|
|
|
<enum name="ATTENDEDTRANSFER"/>
|
|
|
|
<enum name="PICKUP"/>
|
|
|
|
<enum name="FORWARD"/>
|
|
|
|
<enum name="LINKEDID_END"/>
|
2013-07-20 13:25:05 +00:00
|
|
|
<enum name="LOCAL_OPTIMIZE"/>
|
2013-06-13 13:15:56 +00:00
|
|
|
</enumlist>
|
|
|
|
</description>
|
|
|
|
</configOption>
|
|
|
|
</configObject>
|
|
|
|
</configFile>
|
|
|
|
</configInfo>
|
|
|
|
***/
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! Message router for state that CEL needs to know about */
|
|
|
|
static struct stasis_message_router *cel_state_router;
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
/*! Topic for CEL-specific messages */
|
|
|
|
static struct stasis_topic *cel_topic;
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! Aggregation topic for all topics CEL needs to know about */
|
2013-06-25 13:03:17 +00:00
|
|
|
static struct stasis_topic *cel_aggregation_topic;
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! Subscription for forwarding the channel caching topic */
|
Multiple revisions 399887,400138,400178,400180-400181
........
r399887 | dlee | 2013-09-26 10:41:47 -0500 (Thu, 26 Sep 2013) | 1 line
Minor performance bump by not allocate manager variable struct if we don't need it
........
r400138 | dlee | 2013-09-30 10:24:00 -0500 (Mon, 30 Sep 2013) | 23 lines
Stasis performance improvements
This patch addresses several performance problems that were found in
the initial performance testing of Asterisk 12.
The Stasis dispatch object was allocated as an AO2 object, even though
it has a very confined lifecycle. This was replaced with a straight
ast_malloc().
The Stasis message router was spending an inordinate amount of time
searching hash tables. In this case, most of our routers had 6 or
fewer routes in them to begin with. This was replaced with an array
that's searched linearly for the route.
We more heavily rely on AO2 objects in Asterisk 12, and the memset()
in ao2_ref() actually became noticeable on the profile. This was
#ifdef'ed to only run when AO2_DEBUG was enabled.
After being misled by an erroneous comment in taskprocessor.c during
profiling, the wrong comment was removed.
Review: https://reviewboard.asterisk.org/r/2873/
........
r400178 | dlee | 2013-09-30 13:26:27 -0500 (Mon, 30 Sep 2013) | 24 lines
Taskprocessor optimization; switch Stasis to use taskprocessors
This patch optimizes taskprocessor to use a semaphore for signaling,
which the OS can do a better job at managing contention and waiting
that we can with a mutex and condition.
The taskprocessor execution was also slightly optimized to reduce the
number of locks taken.
The only observable difference in the taskprocessor implementation is
that when the final reference to the taskprocessor goes away, it will
execute all tasks to completion instead of discarding the unexecuted
tasks.
For systems where unnamed semaphores are not supported, a really
simple semaphore implementation is provided. (Which gives identical
performance as the original taskprocessor implementation).
The way we ended up implementing Stasis caused the threadpool to be a
burden instead of a boost to performance. This was switched to just
use taskprocessors directly for subscriptions.
Review: https://reviewboard.asterisk.org/r/2881/
........
r400180 | dlee | 2013-09-30 13:39:34 -0500 (Mon, 30 Sep 2013) | 28 lines
Optimize how Stasis forwards are dispatched
This patch optimizes how forwards are dispatched in Stasis.
Originally, forwards were dispatched as subscriptions that are invoked
on the publishing thread. This did not account for the vast number of
forwards we would end up having in the system, and the amount of work it
would take to walk though the forward subscriptions.
This patch modifies Stasis so that rather than walking the tree of
forwards on every dispatch, when forwards and subscriptions are changed,
the subscriber list for every topic in the tree is changed.
This has a couple of benefits. First, this reduces the workload of
dispatching messages. It also reduces contention when dispatching to
different topics that happen to forward to the same aggregation topic
(as happens with all of the channel, bridge and endpoint topics).
Since forwards are no longer subscriptions, the bulk of this patch is
simply changing stasis_subscription objects to stasis_forward objects
(which, admittedly, I should have done in the first place.)
Since this required me to yet again put in a growing array, I finally
abstracted that out into a set of ast_vector macros in
asterisk/vector.h.
Review: https://reviewboard.asterisk.org/r/2883/
........
r400181 | dlee | 2013-09-30 13:48:57 -0500 (Mon, 30 Sep 2013) | 28 lines
Remove dispatch object allocation from Stasis publishing
While looking for areas for performance improvement, I realized that an
unused feature in Stasis was negatively impacting performance.
When a message is sent to a subscriber, a dispatch object is allocated
for the dispatch, containing the topic the message was published to, the
subscriber the message is being sent to, and the message itself.
The topic is actually unused by any subscriber in Asterisk today. And
the subscriber is associated with the taskprocessor the message is being
dispatched to.
First, this patch removes the unused topic parameter from Stasis
subscription callbacks.
Second, this patch introduces the concept of taskprocessor local data,
data that may be set on a taskprocessor and provided along with the data
pointer when a task is pushed using the ast_taskprocessor_push_local()
call. This allows the task to have both data specific to that
taskprocessor, in addition to data specific to that invocation.
With those two changes, the dispatch object can be removed completely,
and the message is simply refcounted and sent directly to the
taskprocessor.
Review: https://reviewboard.asterisk.org/r/2884/
........
Merged revisions 399887,400138,400178,400180-400181 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400186 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-09-30 18:55:27 +00:00
|
|
|
static struct stasis_forward *cel_channel_forwarder;
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
/*! Subscription for forwarding the channel caching topic */
|
Multiple revisions 399887,400138,400178,400180-400181
........
r399887 | dlee | 2013-09-26 10:41:47 -0500 (Thu, 26 Sep 2013) | 1 line
Minor performance bump by not allocate manager variable struct if we don't need it
........
r400138 | dlee | 2013-09-30 10:24:00 -0500 (Mon, 30 Sep 2013) | 23 lines
Stasis performance improvements
This patch addresses several performance problems that were found in
the initial performance testing of Asterisk 12.
The Stasis dispatch object was allocated as an AO2 object, even though
it has a very confined lifecycle. This was replaced with a straight
ast_malloc().
The Stasis message router was spending an inordinate amount of time
searching hash tables. In this case, most of our routers had 6 or
fewer routes in them to begin with. This was replaced with an array
that's searched linearly for the route.
We more heavily rely on AO2 objects in Asterisk 12, and the memset()
in ao2_ref() actually became noticeable on the profile. This was
#ifdef'ed to only run when AO2_DEBUG was enabled.
After being misled by an erroneous comment in taskprocessor.c during
profiling, the wrong comment was removed.
Review: https://reviewboard.asterisk.org/r/2873/
........
r400178 | dlee | 2013-09-30 13:26:27 -0500 (Mon, 30 Sep 2013) | 24 lines
Taskprocessor optimization; switch Stasis to use taskprocessors
This patch optimizes taskprocessor to use a semaphore for signaling,
which the OS can do a better job at managing contention and waiting
that we can with a mutex and condition.
The taskprocessor execution was also slightly optimized to reduce the
number of locks taken.
The only observable difference in the taskprocessor implementation is
that when the final reference to the taskprocessor goes away, it will
execute all tasks to completion instead of discarding the unexecuted
tasks.
For systems where unnamed semaphores are not supported, a really
simple semaphore implementation is provided. (Which gives identical
performance as the original taskprocessor implementation).
The way we ended up implementing Stasis caused the threadpool to be a
burden instead of a boost to performance. This was switched to just
use taskprocessors directly for subscriptions.
Review: https://reviewboard.asterisk.org/r/2881/
........
r400180 | dlee | 2013-09-30 13:39:34 -0500 (Mon, 30 Sep 2013) | 28 lines
Optimize how Stasis forwards are dispatched
This patch optimizes how forwards are dispatched in Stasis.
Originally, forwards were dispatched as subscriptions that are invoked
on the publishing thread. This did not account for the vast number of
forwards we would end up having in the system, and the amount of work it
would take to walk though the forward subscriptions.
This patch modifies Stasis so that rather than walking the tree of
forwards on every dispatch, when forwards and subscriptions are changed,
the subscriber list for every topic in the tree is changed.
This has a couple of benefits. First, this reduces the workload of
dispatching messages. It also reduces contention when dispatching to
different topics that happen to forward to the same aggregation topic
(as happens with all of the channel, bridge and endpoint topics).
Since forwards are no longer subscriptions, the bulk of this patch is
simply changing stasis_subscription objects to stasis_forward objects
(which, admittedly, I should have done in the first place.)
Since this required me to yet again put in a growing array, I finally
abstracted that out into a set of ast_vector macros in
asterisk/vector.h.
Review: https://reviewboard.asterisk.org/r/2883/
........
r400181 | dlee | 2013-09-30 13:48:57 -0500 (Mon, 30 Sep 2013) | 28 lines
Remove dispatch object allocation from Stasis publishing
While looking for areas for performance improvement, I realized that an
unused feature in Stasis was negatively impacting performance.
When a message is sent to a subscriber, a dispatch object is allocated
for the dispatch, containing the topic the message was published to, the
subscriber the message is being sent to, and the message itself.
The topic is actually unused by any subscriber in Asterisk today. And
the subscriber is associated with the taskprocessor the message is being
dispatched to.
First, this patch removes the unused topic parameter from Stasis
subscription callbacks.
Second, this patch introduces the concept of taskprocessor local data,
data that may be set on a taskprocessor and provided along with the data
pointer when a task is pushed using the ast_taskprocessor_push_local()
call. This allows the task to have both data specific to that
taskprocessor, in addition to data specific to that invocation.
With those two changes, the dispatch object can be removed completely,
and the message is simply refcounted and sent directly to the
taskprocessor.
Review: https://reviewboard.asterisk.org/r/2884/
........
Merged revisions 399887,400138,400178,400180-400181 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400186 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-09-30 18:55:27 +00:00
|
|
|
static struct stasis_forward *cel_bridge_forwarder;
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2013-06-13 13:46:40 +00:00
|
|
|
/*! Subscription for forwarding the parking topic */
|
Multiple revisions 399887,400138,400178,400180-400181
........
r399887 | dlee | 2013-09-26 10:41:47 -0500 (Thu, 26 Sep 2013) | 1 line
Minor performance bump by not allocate manager variable struct if we don't need it
........
r400138 | dlee | 2013-09-30 10:24:00 -0500 (Mon, 30 Sep 2013) | 23 lines
Stasis performance improvements
This patch addresses several performance problems that were found in
the initial performance testing of Asterisk 12.
The Stasis dispatch object was allocated as an AO2 object, even though
it has a very confined lifecycle. This was replaced with a straight
ast_malloc().
The Stasis message router was spending an inordinate amount of time
searching hash tables. In this case, most of our routers had 6 or
fewer routes in them to begin with. This was replaced with an array
that's searched linearly for the route.
We more heavily rely on AO2 objects in Asterisk 12, and the memset()
in ao2_ref() actually became noticeable on the profile. This was
#ifdef'ed to only run when AO2_DEBUG was enabled.
After being misled by an erroneous comment in taskprocessor.c during
profiling, the wrong comment was removed.
Review: https://reviewboard.asterisk.org/r/2873/
........
r400178 | dlee | 2013-09-30 13:26:27 -0500 (Mon, 30 Sep 2013) | 24 lines
Taskprocessor optimization; switch Stasis to use taskprocessors
This patch optimizes taskprocessor to use a semaphore for signaling,
which the OS can do a better job at managing contention and waiting
that we can with a mutex and condition.
The taskprocessor execution was also slightly optimized to reduce the
number of locks taken.
The only observable difference in the taskprocessor implementation is
that when the final reference to the taskprocessor goes away, it will
execute all tasks to completion instead of discarding the unexecuted
tasks.
For systems where unnamed semaphores are not supported, a really
simple semaphore implementation is provided. (Which gives identical
performance as the original taskprocessor implementation).
The way we ended up implementing Stasis caused the threadpool to be a
burden instead of a boost to performance. This was switched to just
use taskprocessors directly for subscriptions.
Review: https://reviewboard.asterisk.org/r/2881/
........
r400180 | dlee | 2013-09-30 13:39:34 -0500 (Mon, 30 Sep 2013) | 28 lines
Optimize how Stasis forwards are dispatched
This patch optimizes how forwards are dispatched in Stasis.
Originally, forwards were dispatched as subscriptions that are invoked
on the publishing thread. This did not account for the vast number of
forwards we would end up having in the system, and the amount of work it
would take to walk though the forward subscriptions.
This patch modifies Stasis so that rather than walking the tree of
forwards on every dispatch, when forwards and subscriptions are changed,
the subscriber list for every topic in the tree is changed.
This has a couple of benefits. First, this reduces the workload of
dispatching messages. It also reduces contention when dispatching to
different topics that happen to forward to the same aggregation topic
(as happens with all of the channel, bridge and endpoint topics).
Since forwards are no longer subscriptions, the bulk of this patch is
simply changing stasis_subscription objects to stasis_forward objects
(which, admittedly, I should have done in the first place.)
Since this required me to yet again put in a growing array, I finally
abstracted that out into a set of ast_vector macros in
asterisk/vector.h.
Review: https://reviewboard.asterisk.org/r/2883/
........
r400181 | dlee | 2013-09-30 13:48:57 -0500 (Mon, 30 Sep 2013) | 28 lines
Remove dispatch object allocation from Stasis publishing
While looking for areas for performance improvement, I realized that an
unused feature in Stasis was negatively impacting performance.
When a message is sent to a subscriber, a dispatch object is allocated
for the dispatch, containing the topic the message was published to, the
subscriber the message is being sent to, and the message itself.
The topic is actually unused by any subscriber in Asterisk today. And
the subscriber is associated with the taskprocessor the message is being
dispatched to.
First, this patch removes the unused topic parameter from Stasis
subscription callbacks.
Second, this patch introduces the concept of taskprocessor local data,
data that may be set on a taskprocessor and provided along with the data
pointer when a task is pushed using the ast_taskprocessor_push_local()
call. This allows the task to have both data specific to that
taskprocessor, in addition to data specific to that invocation.
With those two changes, the dispatch object can be removed completely,
and the message is simply refcounted and sent directly to the
taskprocessor.
Review: https://reviewboard.asterisk.org/r/2884/
........
Merged revisions 399887,400138,400178,400180-400181 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400186 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-09-30 18:55:27 +00:00
|
|
|
static struct stasis_forward *cel_parking_forwarder;
|
2013-06-13 13:46:40 +00:00
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
/*! Subscription for forwarding the CEL-specific topic */
|
Multiple revisions 399887,400138,400178,400180-400181
........
r399887 | dlee | 2013-09-26 10:41:47 -0500 (Thu, 26 Sep 2013) | 1 line
Minor performance bump by not allocate manager variable struct if we don't need it
........
r400138 | dlee | 2013-09-30 10:24:00 -0500 (Mon, 30 Sep 2013) | 23 lines
Stasis performance improvements
This patch addresses several performance problems that were found in
the initial performance testing of Asterisk 12.
The Stasis dispatch object was allocated as an AO2 object, even though
it has a very confined lifecycle. This was replaced with a straight
ast_malloc().
The Stasis message router was spending an inordinate amount of time
searching hash tables. In this case, most of our routers had 6 or
fewer routes in them to begin with. This was replaced with an array
that's searched linearly for the route.
We more heavily rely on AO2 objects in Asterisk 12, and the memset()
in ao2_ref() actually became noticeable on the profile. This was
#ifdef'ed to only run when AO2_DEBUG was enabled.
After being misled by an erroneous comment in taskprocessor.c during
profiling, the wrong comment was removed.
Review: https://reviewboard.asterisk.org/r/2873/
........
r400178 | dlee | 2013-09-30 13:26:27 -0500 (Mon, 30 Sep 2013) | 24 lines
Taskprocessor optimization; switch Stasis to use taskprocessors
This patch optimizes taskprocessor to use a semaphore for signaling,
which the OS can do a better job at managing contention and waiting
that we can with a mutex and condition.
The taskprocessor execution was also slightly optimized to reduce the
number of locks taken.
The only observable difference in the taskprocessor implementation is
that when the final reference to the taskprocessor goes away, it will
execute all tasks to completion instead of discarding the unexecuted
tasks.
For systems where unnamed semaphores are not supported, a really
simple semaphore implementation is provided. (Which gives identical
performance as the original taskprocessor implementation).
The way we ended up implementing Stasis caused the threadpool to be a
burden instead of a boost to performance. This was switched to just
use taskprocessors directly for subscriptions.
Review: https://reviewboard.asterisk.org/r/2881/
........
r400180 | dlee | 2013-09-30 13:39:34 -0500 (Mon, 30 Sep 2013) | 28 lines
Optimize how Stasis forwards are dispatched
This patch optimizes how forwards are dispatched in Stasis.
Originally, forwards were dispatched as subscriptions that are invoked
on the publishing thread. This did not account for the vast number of
forwards we would end up having in the system, and the amount of work it
would take to walk though the forward subscriptions.
This patch modifies Stasis so that rather than walking the tree of
forwards on every dispatch, when forwards and subscriptions are changed,
the subscriber list for every topic in the tree is changed.
This has a couple of benefits. First, this reduces the workload of
dispatching messages. It also reduces contention when dispatching to
different topics that happen to forward to the same aggregation topic
(as happens with all of the channel, bridge and endpoint topics).
Since forwards are no longer subscriptions, the bulk of this patch is
simply changing stasis_subscription objects to stasis_forward objects
(which, admittedly, I should have done in the first place.)
Since this required me to yet again put in a growing array, I finally
abstracted that out into a set of ast_vector macros in
asterisk/vector.h.
Review: https://reviewboard.asterisk.org/r/2883/
........
r400181 | dlee | 2013-09-30 13:48:57 -0500 (Mon, 30 Sep 2013) | 28 lines
Remove dispatch object allocation from Stasis publishing
While looking for areas for performance improvement, I realized that an
unused feature in Stasis was negatively impacting performance.
When a message is sent to a subscriber, a dispatch object is allocated
for the dispatch, containing the topic the message was published to, the
subscriber the message is being sent to, and the message itself.
The topic is actually unused by any subscriber in Asterisk today. And
the subscriber is associated with the taskprocessor the message is being
dispatched to.
First, this patch removes the unused topic parameter from Stasis
subscription callbacks.
Second, this patch introduces the concept of taskprocessor local data,
data that may be set on a taskprocessor and provided along with the data
pointer when a task is pushed using the ast_taskprocessor_push_local()
call. This allows the task to have both data specific to that
taskprocessor, in addition to data specific to that invocation.
With those two changes, the dispatch object can be removed completely,
and the message is simply refcounted and sent directly to the
taskprocessor.
Review: https://reviewboard.asterisk.org/r/2884/
........
Merged revisions 399887,400138,400178,400180-400181 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400186 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-09-30 18:55:27 +00:00
|
|
|
static struct stasis_forward *cel_cel_forwarder;
|
2013-06-25 13:03:17 +00:00
|
|
|
|
|
|
|
struct stasis_message_type *cel_generic_type(void);
|
|
|
|
STASIS_MESSAGE_TYPE_DEFN(cel_generic_type);
|
|
|
|
|
2013-08-17 14:46:44 +00:00
|
|
|
/*! Container for CEL backend information */
|
2014-01-24 23:33:26 +00:00
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cel_backends);
|
2013-08-17 14:46:44 +00:00
|
|
|
|
|
|
|
/*! The number of buckets into which backend names will be hashed */
|
|
|
|
#define BACKEND_BUCKETS 13
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
/*! Container for dial end multichannel blobs for holding on to dial statuses */
|
2014-01-24 23:33:26 +00:00
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cel_dialstatus_store);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Maximum possible CEL event IDs
|
|
|
|
* \note This limit is currently imposed by the eventset definition
|
|
|
|
*/
|
|
|
|
#define CEL_MAX_EVENT_IDS 64
|
|
|
|
|
2012-03-22 19:51:16 +00:00
|
|
|
/*!
|
2013-06-13 13:15:56 +00:00
|
|
|
* \brief Number of buckets for the appset container
|
2009-06-26 15:28:53 +00:00
|
|
|
*/
|
2013-06-13 13:15:56 +00:00
|
|
|
#define NUM_APP_BUCKETS 97
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
/*!
|
2013-06-13 13:15:56 +00:00
|
|
|
* \brief Number of buckets for the dialstatus container
|
2009-06-26 15:28:53 +00:00
|
|
|
*/
|
2013-06-13 13:15:56 +00:00
|
|
|
#define NUM_DIALSTATUS_BUCKETS 251
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
struct cel_linkedid {
|
|
|
|
/*! Number of channels with this linkedid. */
|
|
|
|
unsigned int count;
|
|
|
|
/*! Linkedid stored at end of struct. */
|
|
|
|
char id[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
/*! Container of channel references to a linkedid for CEL purposes. */
|
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
struct cel_dialstatus {
|
|
|
|
/*! Uniqueid of the channel */
|
|
|
|
char uniqueid[AST_MAX_UNIQUEID];
|
|
|
|
/*! The dial status */
|
|
|
|
char dialstatus[0];
|
|
|
|
};
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! \brief Destructor for cel_config */
|
|
|
|
static void cel_general_config_dtor(void *obj)
|
|
|
|
{
|
2013-07-02 14:01:53 +00:00
|
|
|
struct ast_cel_general_config *cfg = obj;
|
2013-06-13 13:15:56 +00:00
|
|
|
ast_string_field_free_memory(cfg);
|
|
|
|
ao2_cleanup(cfg->apps);
|
|
|
|
cfg->apps = NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
void *ast_cel_general_config_alloc(void)
|
2013-06-13 13:15:56 +00:00
|
|
|
{
|
2013-07-02 14:01:53 +00:00
|
|
|
RAII_VAR(struct ast_cel_general_config *, cfg, NULL, ao2_cleanup);
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
if (!(cfg = ao2_alloc(sizeof(*cfg), cel_general_config_dtor))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_string_field_init(cfg, 64)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cfg->apps = ast_str_container_alloc(NUM_APP_BUCKETS))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_ref(cfg, +1);
|
|
|
|
return cfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief A container that holds all config-related information */
|
|
|
|
struct cel_config {
|
2013-07-02 14:01:53 +00:00
|
|
|
struct ast_cel_general_config *general;
|
2013-06-13 13:15:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cel_configs);
|
|
|
|
|
|
|
|
/*! \brief Destructor for cel_config */
|
|
|
|
static void cel_config_dtor(void *obj)
|
|
|
|
{
|
|
|
|
struct cel_config *cfg = obj;
|
|
|
|
ao2_cleanup(cfg->general);
|
|
|
|
cfg->general = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *cel_config_alloc(void)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct cel_config *, cfg, NULL, ao2_cleanup);
|
|
|
|
|
|
|
|
if (!(cfg = ao2_alloc(sizeof(*cfg), cel_config_dtor))) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
if (!(cfg->general = ast_cel_general_config_alloc())) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_ref(cfg, +1);
|
|
|
|
return cfg;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
/*! \brief An aco_type structure to link the "general" category to the ast_cel_general_config type */
|
2013-06-13 13:15:56 +00:00
|
|
|
static struct aco_type general_option = {
|
|
|
|
.type = ACO_GLOBAL,
|
|
|
|
.name = "general",
|
|
|
|
.item_offset = offsetof(struct cel_config, general),
|
2017-12-12 18:55:12 +00:00
|
|
|
.category_match = ACO_WHITELIST_EXACT,
|
|
|
|
.category = "general",
|
|
|
|
};
|
|
|
|
|
|
|
|
/*! Config sections used by existing modules. Do not add to this list. */
|
|
|
|
static const char *ignore_categories[] = {
|
|
|
|
"manager",
|
|
|
|
"radius",
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct aco_type ignore_option = {
|
|
|
|
.type = ACO_IGNORE,
|
|
|
|
.name = "modules",
|
|
|
|
.category = (const char*)ignore_categories,
|
|
|
|
.category_match = ACO_WHITELIST_ARRAY,
|
2013-06-13 13:15:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*! \brief The config file to be processed for the module. */
|
|
|
|
static struct aco_file cel_conf = {
|
|
|
|
.filename = "cel.conf", /*!< The name of the config file */
|
2017-12-12 18:55:12 +00:00
|
|
|
.types = ACO_TYPES(&general_option, &ignore_option), /*!< The mapping object types to be processed */
|
2013-06-13 13:15:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int cel_pre_apply_config(void);
|
|
|
|
|
|
|
|
CONFIG_INFO_CORE("cel", cel_cfg_info, cel_configs, cel_config_alloc,
|
|
|
|
.files = ACO_FILES(&cel_conf),
|
|
|
|
.pre_apply_config = cel_pre_apply_config,
|
|
|
|
);
|
|
|
|
|
|
|
|
static int cel_pre_apply_config(void)
|
|
|
|
{
|
|
|
|
struct cel_config *cfg = aco_pending_config(&cel_cfg_info);
|
|
|
|
|
|
|
|
if (!cfg->general) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ao2_container_count(cfg->general->apps)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_START)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_END)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Applications are listed to be tracked, but APP events are not tracked\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct aco_type *general_options[] = ACO_TYPES(&general_option);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Map of ast_cel_event_type to strings
|
|
|
|
*/
|
2009-07-09 14:10:01 +00:00
|
|
|
static const char * const cel_event_types[CEL_MAX_EVENT_IDS] = {
|
2015-04-09 12:57:21 +00:00
|
|
|
[AST_CEL_ALL] = "ALL",
|
2009-06-26 15:28:53 +00:00
|
|
|
[AST_CEL_CHANNEL_START] = "CHAN_START",
|
|
|
|
[AST_CEL_CHANNEL_END] = "CHAN_END",
|
|
|
|
[AST_CEL_ANSWER] = "ANSWER",
|
|
|
|
[AST_CEL_HANGUP] = "HANGUP",
|
|
|
|
[AST_CEL_APP_START] = "APP_START",
|
|
|
|
[AST_CEL_APP_END] = "APP_END",
|
|
|
|
[AST_CEL_PARK_START] = "PARK_START",
|
|
|
|
[AST_CEL_PARK_END] = "PARK_END",
|
|
|
|
[AST_CEL_USER_DEFINED] = "USER_DEFINED",
|
2013-09-27 14:08:23 +00:00
|
|
|
[AST_CEL_BRIDGE_ENTER] = "BRIDGE_ENTER",
|
|
|
|
[AST_CEL_BRIDGE_EXIT] = "BRIDGE_EXIT",
|
2009-06-26 15:28:53 +00:00
|
|
|
[AST_CEL_BLINDTRANSFER] = "BLINDTRANSFER",
|
|
|
|
[AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER",
|
|
|
|
[AST_CEL_PICKUP] = "PICKUP",
|
|
|
|
[AST_CEL_FORWARD] = "FORWARD",
|
|
|
|
[AST_CEL_LINKEDID_END] = "LINKEDID_END",
|
2013-07-20 13:25:05 +00:00
|
|
|
[AST_CEL_LOCAL_OPTIMIZE] = "LOCAL_OPTIMIZE",
|
2009-06-26 15:28:53 +00:00
|
|
|
};
|
|
|
|
|
2013-08-17 14:46:44 +00:00
|
|
|
struct cel_backend {
|
|
|
|
ast_cel_backend_cb callback; /*!< Callback for this backend */
|
|
|
|
char name[0]; /*!< Name of this backend */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*! \brief Hashing function for cel_backend */
|
2017-12-30 04:59:00 +00:00
|
|
|
AO2_STRING_FIELD_HASH_FN(cel_backend, name)
|
2013-08-17 14:46:44 +00:00
|
|
|
|
|
|
|
/*! \brief Comparator function for cel_backend */
|
2017-12-30 04:59:00 +00:00
|
|
|
AO2_STRING_FIELD_CMP_FN(cel_backend, name)
|
2013-08-17 14:46:44 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! \brief Hashing function for dialstatus container */
|
2017-12-30 04:59:00 +00:00
|
|
|
AO2_STRING_FIELD_HASH_FN(cel_dialstatus, uniqueid)
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
/*! \brief Comparator function for dialstatus container */
|
2017-12-30 04:59:00 +00:00
|
|
|
AO2_STRING_FIELD_CMP_FN(cel_dialstatus, uniqueid)
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
unsigned int ast_cel_check_enabled(void)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
unsigned int enabled;
|
|
|
|
struct cel_config *cfg = ao2_global_obj_ref(cel_configs);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
enabled = (!cfg || !cfg->general) ? 0 : cfg->general->enable;
|
|
|
|
ao2_cleanup(cfg);
|
|
|
|
return enabled;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
2013-06-13 13:15:56 +00:00
|
|
|
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
2014-01-24 23:33:26 +00:00
|
|
|
RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
|
|
|
|
struct ao2_iterator iter;
|
|
|
|
char *app;
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "cel show status";
|
|
|
|
e->usage =
|
|
|
|
"Usage: cel show status\n"
|
|
|
|
" Displays the Channel Event Logging system status.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
case CLI_HANDLER:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc > 3) {
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
ast_cli(a->fd, "CEL Logging: %s\n", ast_cel_check_enabled() ? "Enabled" : "Disabled");
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!cfg || !cfg->general || !cfg->general->enable) {
|
2009-06-26 15:28:53 +00:00
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
for (i = 0; i < (sizeof(cfg->general->events) * 8); i++) {
|
2009-06-26 15:28:53 +00:00
|
|
|
const char *name;
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!(cfg->general->events & ((int64_t) 1 << i))) {
|
2009-06-26 15:28:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = ast_cel_get_type_name(i);
|
|
|
|
if (strcasecmp(name, "Unknown")) {
|
|
|
|
ast_cli(a->fd, "CEL Tracking Event: %s\n", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
iter = ao2_iterator_init(cfg->general->apps, 0);
|
|
|
|
for (; (app = ao2_iterator_next(&iter)); ao2_ref(app, -1)) {
|
|
|
|
ast_cli(a->fd, "CEL Tracking Application: %s\n", app);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&iter);
|
|
|
|
|
|
|
|
if (backends) {
|
|
|
|
struct cel_backend *backend;
|
|
|
|
|
|
|
|
iter = ao2_iterator_init(backends, 0);
|
|
|
|
for (; (backend = ao2_iterator_next(&iter)); ao2_ref(backend, -1)) {
|
|
|
|
ast_cli(a->fd, "CEL Event Subscriber: %s\n", backend->name);
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&iter);
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status");
|
|
|
|
|
|
|
|
enum ast_cel_event_type ast_cel_str_to_event_type(const char *name)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LEN(cel_event_types); i++) {
|
2015-04-09 12:57:21 +00:00
|
|
|
if (cel_event_types[i] && !strcasecmp(name, cel_event_types[i])) {
|
2009-06-26 15:28:53 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-09 12:57:21 +00:00
|
|
|
ast_log(LOG_ERROR, "Unknown event name '%s'\n", name);
|
|
|
|
return AST_CEL_INVALID_VALUE;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ast_cel_track_event(enum ast_cel_event_type et)
|
|
|
|
{
|
2013-06-13 13:15:56 +00:00
|
|
|
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg || !cfg->general) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-17 19:30:06 +00:00
|
|
|
return (cfg->general->events & ((int64_t) 1 << et)) ? 1 : 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static int events_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2013-07-02 14:01:53 +00:00
|
|
|
struct ast_cel_general_config *cfg = obj;
|
2013-06-13 13:15:56 +00:00
|
|
|
char *events = ast_strdupa(var->value);
|
2009-06-26 15:28:53 +00:00
|
|
|
char *cur_event;
|
|
|
|
|
|
|
|
while ((cur_event = strsep(&events, ","))) {
|
|
|
|
enum ast_cel_event_type event_type;
|
|
|
|
|
|
|
|
cur_event = ast_strip(cur_event);
|
|
|
|
if (ast_strlen_zero(cur_event)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
event_type = ast_cel_str_to_event_type(cur_event);
|
|
|
|
|
2015-04-09 12:57:21 +00:00
|
|
|
if (event_type == AST_CEL_ALL) {
|
2009-06-26 15:28:53 +00:00
|
|
|
/* All events */
|
2013-06-13 13:15:56 +00:00
|
|
|
cfg->events = (int64_t) -1;
|
2015-04-09 12:57:21 +00:00
|
|
|
} else if (event_type == AST_CEL_INVALID_VALUE) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return -1;
|
2009-06-26 15:28:53 +00:00
|
|
|
} else {
|
2013-06-13 13:15:56 +00:00
|
|
|
cfg->events |= ((int64_t) 1 << event_type);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
}
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static int apps_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2013-07-02 14:01:53 +00:00
|
|
|
struct ast_cel_general_config *cfg = obj;
|
2013-06-13 13:15:56 +00:00
|
|
|
char *apps = ast_strdupa(var->value);
|
2009-06-26 15:28:53 +00:00
|
|
|
char *cur_app;
|
|
|
|
|
|
|
|
while ((cur_app = strsep(&apps, ","))) {
|
|
|
|
cur_app = ast_strip(cur_app);
|
|
|
|
if (ast_strlen_zero(cur_app)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
cur_app = ast_str_to_lower(cur_app);
|
|
|
|
ast_str_container_add(cfg->apps, cur_app);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
const char *ast_cel_get_type_name(enum ast_cel_event_type type)
|
|
|
|
{
|
|
|
|
return S_OR(cel_event_types[type], "Unknown");
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static int cel_track_app(const char *const_app)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
|
|
|
RAII_VAR(char *, app, NULL, ao2_cleanup);
|
|
|
|
char *app_lower;
|
|
|
|
|
|
|
|
if (!cfg || !cfg->general) {
|
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
app_lower = ast_str_to_lower(ast_strdupa(const_app));
|
2014-01-24 23:33:26 +00:00
|
|
|
app = ao2_find(cfg->general->apps, app_lower, OBJ_SEARCH_KEY);
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!app) {
|
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-06-19 12:55:34 +00:00
|
|
|
static int cel_linkedid_ref(const char *linkedid);
|
2014-01-24 23:33:26 +00:00
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
|
|
|
|
enum ast_cel_event_type event_type, const char *userdefevname,
|
2013-09-27 14:08:23 +00:00
|
|
|
struct ast_json *extra, const char *peer)
|
2013-07-02 14:01:53 +00:00
|
|
|
{
|
|
|
|
struct timeval eventtime = ast_tvnow();
|
2013-08-02 14:27:35 +00:00
|
|
|
RAII_VAR(char *, extra_txt, NULL, ast_json_free);
|
2013-07-20 13:10:22 +00:00
|
|
|
if (extra) {
|
|
|
|
extra_txt = ast_json_dump_string(extra);
|
|
|
|
}
|
2013-07-02 14:01:53 +00:00
|
|
|
return ast_event_new(AST_EVENT_CEL,
|
|
|
|
AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
|
|
|
|
AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
|
|
|
|
AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
|
|
|
|
AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, S_OR(userdefevname, ""),
|
|
|
|
AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_name,
|
|
|
|
AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_number,
|
|
|
|
AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_ani,
|
|
|
|
AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_rdnis,
|
|
|
|
AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_dnid,
|
|
|
|
AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, snapshot->exten,
|
|
|
|
AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, snapshot->context,
|
|
|
|
AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->name,
|
|
|
|
AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->appl,
|
|
|
|
AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, snapshot->data,
|
|
|
|
AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, snapshot->amaflags,
|
|
|
|
AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, snapshot->accountcode,
|
|
|
|
AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peeraccount,
|
|
|
|
AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->uniqueid,
|
|
|
|
AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
|
|
|
|
AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
|
2013-07-20 13:10:22 +00:00
|
|
|
AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
|
2013-09-27 14:08:23 +00:00
|
|
|
AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
|
2013-07-02 14:01:53 +00:00
|
|
|
AST_EVENT_IE_END);
|
|
|
|
}
|
|
|
|
|
2013-08-17 14:46:44 +00:00
|
|
|
static int cel_backend_send_cb(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
struct cel_backend *backend = obj;
|
|
|
|
|
|
|
|
backend->callback(arg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
static int cel_report_event(struct ast_channel_snapshot *snapshot,
|
2013-06-13 13:15:56 +00:00
|
|
|
enum ast_cel_event_type event_type, const char *userdefevname,
|
2013-09-27 14:08:23 +00:00
|
|
|
struct ast_json *extra, const char *peer_str)
|
2013-06-13 13:15:56 +00:00
|
|
|
{
|
|
|
|
struct ast_event *ev;
|
|
|
|
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
2014-01-24 23:33:26 +00:00
|
|
|
RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!cfg || !cfg->general || !cfg->general->enable || !backends) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
|
|
|
|
* reporting on CHANNEL_START so we can track when to send LINKEDID_END */
|
2014-01-24 23:33:26 +00:00
|
|
|
if (event_type == AST_CEL_CHANNEL_START
|
|
|
|
&& ast_cel_track_event(AST_CEL_LINKEDID_END)) {
|
|
|
|
if (cel_linkedid_ref(snapshot->linkedid)) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!ast_cel_track_event(event_type)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if ((event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END)
|
|
|
|
&& !cel_track_app(snapshot->appl)) {
|
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra, peer_str);
|
2013-08-17 14:46:44 +00:00
|
|
|
if (!ev) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return -1;
|
2010-08-23 13:09:47 +00:00
|
|
|
}
|
|
|
|
|
2013-08-17 14:46:44 +00:00
|
|
|
/* Distribute event to backends */
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_callback(backends, OBJ_MULTIPLE | OBJ_NODATA, cel_backend_send_cb, ev);
|
2013-08-17 14:46:44 +00:00
|
|
|
ast_event_destroy(ev);
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
return 0;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* called whenever a channel is destroyed or a linkedid is changed to
|
|
|
|
* potentially emit a CEL_LINKEDID_END event */
|
2013-06-13 13:15:56 +00:00
|
|
|
static void check_retire_linkedid(struct ast_channel_snapshot *snapshot)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
|
|
|
|
struct cel_linkedid *lid;
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!linkedids || ast_strlen_zero(snapshot->linkedid)) {
|
|
|
|
/* The CEL module is shutdown. Abort. */
|
2012-05-02 17:43:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_lock(linkedids);
|
|
|
|
|
|
|
|
lid = ao2_find(linkedids, (void *) snapshot->linkedid, OBJ_SEARCH_KEY);
|
|
|
|
if (!lid) {
|
|
|
|
ao2_unlock(linkedids);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The user may have done a reload to start tracking linkedids
|
|
|
|
* when a call was already in progress. This is an unusual kind
|
|
|
|
* of change to make after starting Asterisk.
|
|
|
|
*/
|
|
|
|
ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n",
|
|
|
|
snapshot->linkedid);
|
2012-05-02 17:43:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!--lid->count) {
|
|
|
|
/* No channels use this linkedid anymore. */
|
|
|
|
ao2_unlink(linkedids, lid);
|
|
|
|
ao2_unlock(linkedids);
|
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
} else {
|
|
|
|
ao2_unlock(linkedids);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
2012-05-02 17:43:16 +00:00
|
|
|
ao2_ref(lid, -1);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2012-07-18 19:18:40 +00:00
|
|
|
/* Note that no 'chan_fixup' function is provided for this datastore type,
|
|
|
|
* because the channels that will use it will never be involved in masquerades.
|
|
|
|
*/
|
|
|
|
static const struct ast_datastore_info fabricated_channel_datastore = {
|
|
|
|
.type = "CEL fabricated channel",
|
2012-07-19 22:08:20 +00:00
|
|
|
.destroy = ast_free_ptr,
|
2012-07-18 19:18:40 +00:00
|
|
|
};
|
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event)
|
|
|
|
{
|
|
|
|
struct varshead *headp;
|
|
|
|
struct ast_var_t *newvariable;
|
2012-02-01 17:42:15 +00:00
|
|
|
const char *mixed_name;
|
2009-06-26 15:28:53 +00:00
|
|
|
char timebuf[30];
|
|
|
|
struct ast_channel *tchan;
|
|
|
|
struct ast_cel_event_record record = {
|
|
|
|
.version = AST_CEL_EVENT_RECORD_VERSION,
|
|
|
|
};
|
2012-07-18 19:18:40 +00:00
|
|
|
struct ast_datastore *datastore;
|
|
|
|
char *app_data;
|
2013-06-13 13:15:56 +00:00
|
|
|
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
|
|
|
|
|
|
|
if (!cfg || !cfg->general) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
/* do not call ast_channel_alloc because this is not really a real channel */
|
|
|
|
if (!(tchan = ast_dummy_channel_alloc())) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-29 16:52:47 +00:00
|
|
|
headp = ast_channel_varshead(tchan);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
/* first, get the variables from the event */
|
|
|
|
if (ast_cel_fill_record(event, &record)) {
|
2011-09-26 19:40:12 +00:00
|
|
|
ast_channel_unref(tchan);
|
2009-06-26 15:28:53 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* next, fill the channel with their data */
|
2012-02-01 17:42:15 +00:00
|
|
|
mixed_name = (record.event_type == AST_CEL_USER_DEFINED)
|
|
|
|
? record.user_defined_name : record.event_name;
|
|
|
|
if ((newvariable = ast_var_assign("eventtype", mixed_name))) {
|
2009-06-26 15:28:53 +00:00
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if (ast_strlen_zero(cfg->general->date_format)) {
|
2010-03-20 12:03:07 +00:00
|
|
|
snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec,
|
2009-07-23 01:31:18 +00:00
|
|
|
(long) record.event_time.tv_usec);
|
2009-06-26 15:28:53 +00:00
|
|
|
} else {
|
|
|
|
struct ast_tm tm;
|
|
|
|
ast_localtime(&record.event_time, &tm, NULL);
|
2013-06-13 13:15:56 +00:00
|
|
|
ast_strftime(timebuf, sizeof(timebuf), cfg->general->date_format, &tm);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((newvariable = ast_var_assign("eventtime", timebuf))) {
|
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
|
|
|
|
2012-02-01 17:42:15 +00:00
|
|
|
if ((newvariable = ast_var_assign("eventenum", record.event_name))) {
|
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
2012-01-13 17:36:44 +00:00
|
|
|
if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) {
|
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
if ((newvariable = ast_var_assign("eventextra", record.extra))) {
|
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
|
|
|
|
2012-02-29 16:52:47 +00:00
|
|
|
ast_channel_caller(tchan)->id.name.valid = 1;
|
|
|
|
ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name);
|
|
|
|
ast_channel_caller(tchan)->id.number.valid = 1;
|
|
|
|
ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num);
|
|
|
|
ast_channel_caller(tchan)->ani.number.valid = 1;
|
|
|
|
ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani);
|
|
|
|
ast_channel_redirecting(tchan)->from.number.valid = 1;
|
|
|
|
ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis);
|
|
|
|
ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2012-02-13 17:27:06 +00:00
|
|
|
ast_channel_exten_set(tchan, record.extension);
|
|
|
|
ast_channel_context_set(tchan, record.context);
|
2012-01-09 22:15:50 +00:00
|
|
|
ast_channel_name_set(tchan, record.channel_name);
|
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
|
|
|
ast_channel_internal_set_fake_ids(tchan, record.unique_id, record.linked_id);
|
2012-01-24 20:12:09 +00:00
|
|
|
ast_channel_accountcode_set(tchan, record.account_code);
|
|
|
|
ast_channel_peeraccount_set(tchan, record.peer_account);
|
|
|
|
ast_channel_userfield_set(tchan, record.user_field);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2012-01-11 22:53:09 +00:00
|
|
|
if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) {
|
|
|
|
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2012-02-20 23:43:27 +00:00
|
|
|
ast_channel_amaflags_set(tchan, record.amaflag);
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2012-07-18 19:18:40 +00:00
|
|
|
/* We need to store an 'application name' and 'application
|
|
|
|
* data' on the channel for logging purposes, but the channel
|
|
|
|
* structure only provides a place to store pointers, and it
|
|
|
|
* expects these pointers to be pointing to data that does not
|
|
|
|
* need to be freed. This means that the channel's destructor
|
|
|
|
* does not attempt to free any storage that these pointers
|
|
|
|
* point to. However, we can't provide data in that form directly for
|
|
|
|
* these structure members. In order to ensure that these data
|
|
|
|
* elements have a lifetime that matches the channel's
|
|
|
|
* lifetime, we'll put them in a datastore attached to the
|
|
|
|
* channel, and set's the channel's pointers to point into the
|
|
|
|
* datastore. The datastore will then be automatically destroyed
|
|
|
|
* when the channel is destroyed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) {
|
|
|
|
ast_channel_unref(tchan);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) {
|
|
|
|
ast_datastore_free(datastore);
|
|
|
|
ast_channel_unref(tchan);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-07-19 22:25:00 +00:00
|
|
|
ast_channel_appl_set(tchan, strcpy(app_data, record.application_name));
|
|
|
|
ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1,
|
|
|
|
record.application_data));
|
2012-07-18 19:18:40 +00:00
|
|
|
|
|
|
|
datastore->data = app_data;
|
|
|
|
ast_channel_datastore_add(tchan, datastore);
|
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
return tchan;
|
|
|
|
}
|
|
|
|
|
2013-06-19 12:55:34 +00:00
|
|
|
static int cel_linkedid_ref(const char *linkedid)
|
2012-05-22 17:29:12 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
|
|
|
|
struct cel_linkedid *lid;
|
2012-05-22 17:29:12 +00:00
|
|
|
|
|
|
|
if (ast_strlen_zero(linkedid)) {
|
|
|
|
ast_log(LOG_ERROR, "The linkedid should never be empty\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!linkedids) {
|
|
|
|
/* The CEL module is shutdown. Abort. */
|
|
|
|
return -1;
|
|
|
|
}
|
2012-05-22 17:29:12 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_lock(linkedids);
|
|
|
|
lid = ao2_find(linkedids, (void *) linkedid, OBJ_SEARCH_KEY);
|
|
|
|
if (!lid) {
|
|
|
|
/*
|
|
|
|
* Changes to the lid->count member are protected by the
|
|
|
|
* container lock so the lid object does not need its own lock.
|
|
|
|
*/
|
|
|
|
lid = ao2_alloc_options(sizeof(*lid) + strlen(linkedid) + 1, NULL,
|
|
|
|
AO2_ALLOC_OPT_LOCK_NOLOCK);
|
|
|
|
if (!lid) {
|
|
|
|
ao2_unlock(linkedids);
|
2012-05-22 17:29:12 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
strcpy(lid->id, linkedid);/* Safe */
|
|
|
|
|
|
|
|
ao2_link(linkedids, lid);
|
2012-05-22 17:29:12 +00:00
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
++lid->count;
|
|
|
|
ao2_unlock(linkedids);
|
|
|
|
ao2_ref(lid, -1);
|
|
|
|
|
2012-05-22 17:29:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-26 15:28:53 +00:00
|
|
|
int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r)
|
|
|
|
{
|
|
|
|
if (r->version != AST_CEL_EVENT_RECORD_VERSION) {
|
|
|
|
ast_log(LOG_ERROR, "Module ABI mismatch for ast_cel_event_record. "
|
|
|
|
"Please ensure all modules were compiled for "
|
|
|
|
"this version of Asterisk.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
r->event_type = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TYPE);
|
|
|
|
|
|
|
|
r->event_time.tv_sec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME);
|
|
|
|
r->event_time.tv_usec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME_USEC);
|
|
|
|
|
2012-02-01 17:42:15 +00:00
|
|
|
r->event_name = ast_cel_get_type_name(r->event_type);
|
2009-06-26 15:28:53 +00:00
|
|
|
if (r->event_type == AST_CEL_USER_DEFINED) {
|
|
|
|
r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME);
|
|
|
|
} else {
|
2012-02-01 17:42:15 +00:00
|
|
|
r->user_defined_name = "";
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r->caller_id_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNAME), "");
|
|
|
|
r->caller_id_num = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNUM), "");
|
|
|
|
r->caller_id_ani = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDANI), "");
|
|
|
|
r->caller_id_rdnis = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDRDNIS), "");
|
|
|
|
r->caller_id_dnid = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDDNID), "");
|
|
|
|
r->extension = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTEN), "");
|
|
|
|
r->context = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CONTEXT), "");
|
|
|
|
r->channel_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CHANNAME), "");
|
|
|
|
r->application_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPNAME), "");
|
|
|
|
r->application_data = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPDATA), "");
|
|
|
|
r->account_code = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), "");
|
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
|
|
|
r->peer_account = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEERACCT), "");
|
2009-06-26 15:28:53 +00:00
|
|
|
r->unique_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), "");
|
|
|
|
r->linked_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), "");
|
|
|
|
r->amaflag = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS);
|
|
|
|
r->user_field = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), "");
|
|
|
|
r->peer = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), "");
|
|
|
|
r->extra = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTRA), "");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! \brief Typedef for callbacks that get called on channel snapshot updates */
|
|
|
|
typedef void (*cel_channel_snapshot_monitor)(
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *new_snapshot);
|
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
static struct cel_dialstatus *get_dialstatus(const char *uniqueid)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
|
2016-06-07 23:45:37 +00:00
|
|
|
struct cel_dialstatus *dialstatus = NULL;
|
2014-01-24 23:33:26 +00:00
|
|
|
|
|
|
|
if (dial_statuses) {
|
2016-06-07 23:45:37 +00:00
|
|
|
dialstatus = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_ref(dial_statuses, -1);
|
|
|
|
}
|
2016-06-07 23:45:37 +00:00
|
|
|
return dialstatus;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2013-06-13 13:15:56 +00:00
|
|
|
struct ast_json *json = ast_multi_channel_blob_get_json(blob);
|
|
|
|
if (!json) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-06-26 15:28:53 +00:00
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
json = ast_json_object_get(json, varname);
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!json) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ast_json_string_get(json);
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
/*! \brief Handle channel state changes */
|
|
|
|
static void cel_channel_state_change(
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *new_snapshot)
|
|
|
|
{
|
|
|
|
int is_hungup, was_hungup;
|
2012-05-02 17:43:16 +00:00
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!new_snapshot) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {
|
|
|
|
check_retire_linkedid(old_snapshot);
|
|
|
|
}
|
2013-06-13 13:15:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!old_snapshot) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
|
2013-06-13 13:15:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Handle hangup logic in the Stasis message bus and consumers of Stasis messages
This patch does the following:
* It adds a new soft hangup flag AST_SOFTHANGUP_HANGUP_EXEC that is set when a
channel is executing dialplan hangup logic, i.e., the 'h' extension or a
hangup handler. Stasis messages now also convey the soft hangup flag so
consumers of the messages can know when a channel is executing said
hangup logic.
* It adds a new channel flag, AST_FLAG_DEAD, which is set when a channel is
well and truly dead. Not just a zombie, but dead, Jim. Manager, CEL, CDRs,
and other consumers of Stasis have been updated to look for this flag to
know when the channel should by lying six feet under.
* The CDR engine has been updated to better handle a channel entering and
leaving a bridge. Previously, a new CDR was automatically created when a
channel left a bridge and put into the 'Pending' state; however, this
way of handling CDRs made it difficult for the 'endbeforehexten' logic to
work correctly - there was always a new CDR waiting in the hangup logic
and, even if 'ended', wouldn't be the CDR people wanted to inspect in the
hangup routine. This patch completely removes the Pending state and instead
defers creation of the new CDR until it gets a new message that requires
a new CDR.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393777 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-07-07 20:34:38 +00:00
|
|
|
was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
|
|
|
|
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
if (!was_hungup && is_hungup) {
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra;
|
2016-06-07 23:45:37 +00:00
|
|
|
struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
|
2014-01-24 23:33:26 +00:00
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
extra = ast_json_pack("{s: i, s: s, s: s}",
|
|
|
|
"hangupcause", new_snapshot->hangupcause,
|
|
|
|
"hangupsource", new_snapshot->hangupsource,
|
2016-06-07 23:45:37 +00:00
|
|
|
"dialstatus", dialstatus ? dialstatus->dialstatus : "");
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2016-06-07 23:45:37 +00:00
|
|
|
ao2_cleanup(dialstatus);
|
2013-06-13 13:15:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
|
2013-06-13 13:15:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_channel_linkedid_change(
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *new_snapshot)
|
|
|
|
{
|
|
|
|
if (!old_snapshot || !new_snapshot) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-19 12:55:34 +00:00
|
|
|
ast_assert(!ast_strlen_zero(new_snapshot->linkedid));
|
|
|
|
ast_assert(!ast_strlen_zero(old_snapshot->linkedid));
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (ast_cel_track_event(AST_CEL_LINKEDID_END)
|
|
|
|
&& strcmp(old_snapshot->linkedid, new_snapshot->linkedid)) {
|
2013-06-19 12:55:34 +00:00
|
|
|
cel_linkedid_ref(new_snapshot->linkedid);
|
2013-06-13 13:15:56 +00:00
|
|
|
check_retire_linkedid(old_snapshot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_channel_app_change(
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *new_snapshot)
|
|
|
|
{
|
|
|
|
if (new_snapshot && old_snapshot
|
|
|
|
&& !strcmp(old_snapshot->appl, new_snapshot->appl)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* old snapshot has an application, end it */
|
|
|
|
if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* new snapshot has an application, start it */
|
|
|
|
if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-16 22:25:33 +00:00
|
|
|
/* \brief Handlers for channel snapshot changes.
|
|
|
|
* \note Order of the handlers matters. Application changes must come before state
|
|
|
|
* changes to ensure that hangup notifications occur after application changes.
|
|
|
|
* Linkedid checking should always come last.
|
|
|
|
*/
|
2013-06-13 13:15:56 +00:00
|
|
|
cel_channel_snapshot_monitor cel_channel_monitors[] = {
|
|
|
|
cel_channel_app_change,
|
2013-07-16 22:25:33 +00:00
|
|
|
cel_channel_state_change,
|
2013-06-13 13:15:56 +00:00
|
|
|
cel_channel_linkedid_change,
|
|
|
|
};
|
|
|
|
|
2013-07-19 19:23:39 +00:00
|
|
|
static int cel_filter_channel_snapshot(struct ast_channel_snapshot *snapshot)
|
|
|
|
{
|
|
|
|
if (!snapshot) {
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-08 14:13:05 +00:00
|
|
|
return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
|
2013-07-19 19:23:39 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct stasis_cache_update *update = stasis_message_data(message);
|
|
|
|
if (ast_channel_snapshot_type() == update->type) {
|
|
|
|
struct ast_channel_snapshot *old_snapshot;
|
|
|
|
struct ast_channel_snapshot *new_snapshot;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
old_snapshot = stasis_message_data(update->old_snapshot);
|
|
|
|
new_snapshot = stasis_message_data(update->new_snapshot);
|
|
|
|
|
2013-07-19 19:23:39 +00:00
|
|
|
if (cel_filter_channel_snapshot(old_snapshot) || cel_filter_channel_snapshot(new_snapshot)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {
|
|
|
|
cel_channel_monitors[i](old_snapshot, new_snapshot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
static struct ast_str *cel_generate_peer_str(
|
|
|
|
struct ast_bridge_snapshot *bridge,
|
|
|
|
struct ast_channel_snapshot *chan)
|
|
|
|
{
|
|
|
|
struct ast_str *peer_str = ast_str_create(32);
|
|
|
|
struct ao2_iterator i;
|
|
|
|
char *current_chan = NULL;
|
|
|
|
|
|
|
|
if (!peer_str) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = ao2_iterator_init(bridge->channels, 0);
|
|
|
|
(current_chan = ao2_iterator_next(&i));
|
|
|
|
ao2_cleanup(current_chan)) {
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_channel_snapshot *current_snapshot;
|
2013-09-27 14:08:23 +00:00
|
|
|
|
|
|
|
/* Don't add the channel for which this message is being generated */
|
|
|
|
if (!strcmp(current_chan, chan->uniqueid)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_snapshot = ast_channel_snapshot_get_latest(current_chan);
|
|
|
|
if (!current_snapshot) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_cleanup(current_snapshot);
|
2013-09-27 14:08:23 +00:00
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&i);
|
|
|
|
|
|
|
|
/* Rip off the trailing comma */
|
|
|
|
ast_str_truncate(peer_str, -1);
|
|
|
|
|
|
|
|
return peer_str;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static void cel_bridge_enter_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_bridge_blob *blob = stasis_message_data(message);
|
|
|
|
struct ast_bridge_snapshot *snapshot = blob->bridge;
|
|
|
|
struct ast_channel_snapshot *chan_snapshot = blob->channel;
|
2013-08-22 17:13:16 +00:00
|
|
|
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
|
2013-09-27 14:08:23 +00:00
|
|
|
RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2013-07-19 19:23:39 +00:00
|
|
|
if (cel_filter_channel_snapshot(chan_snapshot)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-26 14:48:21 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
|
|
|
"bridge_id", snapshot->uniqueid,
|
|
|
|
"bridge_technology", snapshot->technology);
|
2013-08-22 17:13:16 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
2013-08-22 17:13:16 +00:00
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
|
|
|
|
if (!peer_str) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_bridge_leave_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_bridge_blob *blob = stasis_message_data(message);
|
|
|
|
struct ast_bridge_snapshot *snapshot = blob->bridge;
|
|
|
|
struct ast_channel_snapshot *chan_snapshot = blob->channel;
|
2013-08-22 17:13:16 +00:00
|
|
|
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
|
2013-09-27 14:08:23 +00:00
|
|
|
RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2013-07-19 19:23:39 +00:00
|
|
|
if (cel_filter_channel_snapshot(chan_snapshot)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-26 14:48:21 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
|
|
|
"bridge_id", snapshot->uniqueid,
|
|
|
|
"bridge_technology", snapshot->technology);
|
2013-08-22 17:13:16 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
2013-06-13 13:46:40 +00:00
|
|
|
}
|
2013-08-22 17:13:16 +00:00
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
|
|
|
|
if (!peer_str) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
|
2013-06-13 13:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_parking_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_parked_call_payload *parked_payload = stasis_message_data(message);
|
2013-07-20 13:10:22 +00:00
|
|
|
RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
|
|
|
|
const char *reason = NULL;
|
2013-06-13 13:46:40 +00:00
|
|
|
|
|
|
|
switch (parked_payload->event_type) {
|
|
|
|
case PARKED_CALL:
|
2013-07-20 13:10:22 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
|
|
|
"parker_dial_string", parked_payload->parker_dial_string,
|
|
|
|
"parking_lot", parked_payload->parkinglot);
|
|
|
|
if (extra) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
|
|
|
return;
|
2013-06-13 13:46:40 +00:00
|
|
|
case PARKED_CALL_TIMEOUT:
|
2013-07-20 13:10:22 +00:00
|
|
|
reason = "ParkedCallTimeOut";
|
2013-06-13 13:46:40 +00:00
|
|
|
break;
|
|
|
|
case PARKED_CALL_GIVEUP:
|
2013-07-20 13:10:22 +00:00
|
|
|
reason = "ParkedCallGiveUp";
|
2013-06-13 13:46:40 +00:00
|
|
|
break;
|
|
|
|
case PARKED_CALL_UNPARKED:
|
2013-07-20 13:10:22 +00:00
|
|
|
reason = "ParkedCallUnparked";
|
2013-06-13 13:46:40 +00:00
|
|
|
break;
|
|
|
|
case PARKED_CALL_FAILED:
|
2013-07-20 13:10:22 +00:00
|
|
|
reason = "ParkedCallFailed";
|
2013-06-13 13:46:40 +00:00
|
|
|
break;
|
2013-08-02 14:13:04 +00:00
|
|
|
case PARKED_CALL_SWAP:
|
|
|
|
reason = "ParkedCallSwap";
|
|
|
|
break;
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
2013-07-20 13:10:22 +00:00
|
|
|
|
2014-06-13 12:56:06 +00:00
|
|
|
if (parked_payload->retriever) {
|
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
2016-10-12 21:24:14 +00:00
|
|
|
"reason", reason ?: "",
|
2014-06-13 12:56:06 +00:00
|
|
|
"retriever", parked_payload->retriever->name);
|
|
|
|
} else {
|
2016-10-12 21:24:14 +00:00
|
|
|
extra = ast_json_pack("{s: s}", "reason", reason ?: "");
|
2014-06-13 12:56:06 +00:00
|
|
|
}
|
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
if (extra) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
static void save_dialstatus(struct ast_multi_channel_blob *blob, struct ast_channel_snapshot *snapshot)
|
2013-06-13 13:15:56 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
|
2016-06-07 23:45:37 +00:00
|
|
|
const char *dialstatus_string = get_blob_variable(blob, "dialstatus");
|
|
|
|
struct cel_dialstatus *dialstatus;
|
|
|
|
size_t dialstatus_string_len;
|
2014-01-24 23:33:26 +00:00
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
if (!dial_statuses || ast_strlen_zero(dialstatus_string)) {
|
|
|
|
ao2_cleanup(dial_statuses);
|
|
|
|
return;
|
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
|
|
|
|
if (dialstatus) {
|
|
|
|
if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) {
|
|
|
|
/* In the case of an answer after we already have a dial status we give
|
|
|
|
* priority to the answer since the call was, well, answered. In the case of
|
|
|
|
* failure dial status results we simply let the first failure be the status.
|
|
|
|
*/
|
|
|
|
ao2_unlink(dial_statuses, dialstatus);
|
|
|
|
ao2_ref(dialstatus, -1);
|
|
|
|
} else {
|
|
|
|
ao2_ref(dialstatus, -1);
|
|
|
|
ao2_ref(dial_statuses, -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dialstatus_string_len = strlen(dialstatus_string) + 1;
|
|
|
|
dialstatus = ao2_alloc_options(sizeof(*dialstatus) + dialstatus_string_len, NULL,
|
|
|
|
AO2_ALLOC_OPT_LOCK_NOLOCK);
|
|
|
|
if (!dialstatus) {
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_ref(dial_statuses, -1);
|
2016-06-07 23:45:37 +00:00
|
|
|
return;
|
2014-01-24 23:33:26 +00:00
|
|
|
}
|
2016-06-07 23:45:37 +00:00
|
|
|
|
|
|
|
ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid));
|
|
|
|
ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len);
|
|
|
|
|
|
|
|
ao2_link(dial_statuses, dialstatus);
|
|
|
|
ao2_ref(dialstatus, -1);
|
|
|
|
ao2_ref(dial_statuses, -1);
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
2014-07-07 01:22:44 +00:00
|
|
|
static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
|
|
|
|
{
|
|
|
|
const char *dialstatus = get_blob_variable(blob, "dialstatus");
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (ast_strlen_zero(dialstatus)) {
|
|
|
|
res = 0;
|
|
|
|
} else if (!strcasecmp(dialstatus, "CHANUNAVAIL")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "CONGESTION")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "NOANSWER")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "BUSY")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "ANSWER")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "CANCEL")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "DONTCALL")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "TORTURE")) {
|
|
|
|
res = 1;
|
|
|
|
} else if (!strcasecmp(dialstatus, "INVALIDARGS")) {
|
|
|
|
res = 1;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
static void cel_dial_cb(void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2013-06-13 13:15:56 +00:00
|
|
|
struct ast_multi_channel_blob *blob = stasis_message_data(message);
|
2016-06-07 23:45:37 +00:00
|
|
|
struct ast_channel_snapshot *snapshot;
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2016-06-07 23:45:37 +00:00
|
|
|
snapshot = ast_multi_channel_blob_get_channel(blob, "caller");
|
|
|
|
if (!snapshot || cel_filter_channel_snapshot(snapshot)) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
2013-06-13 13:15:56 +00:00
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra;
|
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
|
|
|
|
if (extra) {
|
2016-06-07 23:45:37 +00:00
|
|
|
cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
2013-06-25 13:03:17 +00:00
|
|
|
}
|
|
|
|
|
2014-07-07 01:22:44 +00:00
|
|
|
if (is_valid_dialstatus(blob)) {
|
2016-06-07 23:45:37 +00:00
|
|
|
save_dialstatus(blob, snapshot);
|
2012-10-02 01:47:16 +00:00
|
|
|
}
|
2013-06-13 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
static void cel_generic_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_channel_blob *obj = stasis_message_data(message);
|
|
|
|
int event_type = ast_json_integer_get(ast_json_object_get(obj->blob, "event_type"));
|
|
|
|
struct ast_json *event_details = ast_json_object_get(obj->blob, "event_details");
|
|
|
|
|
|
|
|
switch (event_type) {
|
|
|
|
case AST_CEL_USER_DEFINED:
|
|
|
|
{
|
|
|
|
const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
|
2013-07-20 13:10:22 +00:00
|
|
|
struct ast_json *extra = ast_json_object_get(event_details, "extra");
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(obj->snapshot, event_type, event, extra, NULL);
|
2013-06-25 13:03:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ast_log(LOG_ERROR, "Unhandled %s event blob\n", ast_cel_get_type_name(event_type));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
static void cel_blind_transfer_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
2014-08-07 15:30:19 +00:00
|
|
|
struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
|
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
|
|
|
struct ast_channel_snapshot *chan_snapshot = transfer_msg->transferer;
|
|
|
|
struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->bridge;
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra;
|
2013-08-29 15:43:23 +00:00
|
|
|
|
2014-08-07 15:30:19 +00:00
|
|
|
if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
|
2013-07-20 13:10:22 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-08-29 15:43:23 +00:00
|
|
|
|
2014-08-14 19:21:51 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
|
2014-08-07 15:30:19 +00:00
|
|
|
"extension", transfer_msg->exten,
|
|
|
|
"context", transfer_msg->context,
|
|
|
|
"bridge_id", bridge_snapshot->uniqueid,
|
2014-08-14 19:21:51 +00:00
|
|
|
"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name : "N/A",
|
|
|
|
"transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->uniqueid : "N/A");
|
2013-07-20 13:10:22 +00:00
|
|
|
if (extra) {
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_attended_transfer_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_attended_transfer_message *xfer = stasis_message_data(message);
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra = NULL;
|
2013-07-20 13:10:22 +00:00
|
|
|
struct ast_bridge_snapshot *bridge1, *bridge2;
|
|
|
|
struct ast_channel_snapshot *channel1, *channel2;
|
|
|
|
|
|
|
|
/* Make sure bridge1 is always non-NULL */
|
|
|
|
if (!xfer->to_transferee.bridge_snapshot) {
|
|
|
|
bridge1 = xfer->to_transfer_target.bridge_snapshot;
|
|
|
|
bridge2 = xfer->to_transferee.bridge_snapshot;
|
|
|
|
channel1 = xfer->to_transfer_target.channel_snapshot;
|
|
|
|
channel2 = xfer->to_transferee.channel_snapshot;
|
|
|
|
} else {
|
|
|
|
bridge1 = xfer->to_transferee.bridge_snapshot;
|
|
|
|
bridge2 = xfer->to_transfer_target.bridge_snapshot;
|
|
|
|
channel1 = xfer->to_transferee.channel_snapshot;
|
|
|
|
channel2 = xfer->to_transfer_target.channel_snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (xfer->dest_type) {
|
|
|
|
case AST_ATTENDED_TRANSFER_DEST_FAIL:
|
|
|
|
return;
|
2013-07-23 15:28:11 +00:00
|
|
|
/* handle these three the same */
|
2013-07-20 13:10:22 +00:00
|
|
|
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
|
|
|
|
case AST_ATTENDED_TRANSFER_DEST_LINK:
|
2013-07-23 15:28:11 +00:00
|
|
|
case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
|
2014-08-14 19:21:51 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
|
2013-07-20 13:10:22 +00:00
|
|
|
"bridge1_id", bridge1->uniqueid,
|
|
|
|
"channel2_name", channel2->name,
|
2014-08-14 19:21:51 +00:00
|
|
|
"channel2_uniqueid", channel2->uniqueid,
|
2014-08-07 15:30:19 +00:00
|
|
|
"bridge2_id", bridge2->uniqueid,
|
|
|
|
"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
|
2014-08-14 19:21:51 +00:00
|
|
|
"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
|
|
|
|
"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
|
|
|
|
"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
|
2013-07-20 13:10:22 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AST_ATTENDED_TRANSFER_DEST_APP:
|
2014-08-07 15:30:19 +00:00
|
|
|
case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
|
2014-08-14 19:21:51 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
|
2013-07-20 13:10:22 +00:00
|
|
|
"bridge1_id", bridge1->uniqueid,
|
|
|
|
"channel2_name", channel2->name,
|
2014-08-14 19:21:51 +00:00
|
|
|
"channel2_uniqueid", channel2->uniqueid,
|
2014-08-07 15:30:19 +00:00
|
|
|
"app", xfer->dest.app,
|
|
|
|
"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
|
2014-08-14 19:21:51 +00:00
|
|
|
"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
|
|
|
|
"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
|
|
|
|
"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
|
2013-07-20 13:10:22 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cel_pickup_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_multi_channel_blob *obj = stasis_message_data(message);
|
|
|
|
struct ast_channel_snapshot *channel = ast_multi_channel_blob_get_channel(obj, "channel");
|
|
|
|
struct ast_channel_snapshot *target = ast_multi_channel_blob_get_channel(obj, "target");
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra;
|
2013-07-20 13:10:22 +00:00
|
|
|
|
|
|
|
if (!channel || !target) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-14 19:21:51 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
|
|
|
"pickup_channel", channel->name,
|
|
|
|
"pickup_channel_uniqueid", channel->uniqueid);
|
2013-08-22 17:13:16 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2013-07-20 13:10:22 +00:00
|
|
|
}
|
|
|
|
|
2013-07-20 13:25:05 +00:00
|
|
|
static void cel_local_cb(
|
|
|
|
void *data, struct stasis_subscription *sub,
|
|
|
|
struct stasis_message *message)
|
|
|
|
{
|
|
|
|
struct ast_multi_channel_blob *obj = stasis_message_data(message);
|
|
|
|
struct ast_channel_snapshot *localone = ast_multi_channel_blob_get_channel(obj, "1");
|
|
|
|
struct ast_channel_snapshot *localtwo = ast_multi_channel_blob_get_channel(obj, "2");
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *extra;
|
2013-07-20 13:25:05 +00:00
|
|
|
|
|
|
|
if (!localone || !localtwo) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-14 19:21:51 +00:00
|
|
|
extra = ast_json_pack("{s: s, s: s}",
|
|
|
|
"local_two", localtwo->name,
|
|
|
|
"local_two_uniqueid", localtwo->uniqueid);
|
2013-08-22 17:13:16 +00:00
|
|
|
if (!extra) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-27 14:08:23 +00:00
|
|
|
cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_json_unref(extra);
|
2013-07-20 13:25:05 +00:00
|
|
|
}
|
|
|
|
|
2013-11-15 14:37:20 +00:00
|
|
|
static void destroy_routes(void)
|
2013-06-13 13:15:56 +00:00
|
|
|
{
|
2013-10-03 18:51:33 +00:00
|
|
|
stasis_message_router_unsubscribe_and_join(cel_state_router);
|
|
|
|
cel_state_router = NULL;
|
2013-11-15 14:37:20 +00:00
|
|
|
}
|
2013-10-02 21:26:34 +00:00
|
|
|
|
2013-11-15 14:37:20 +00:00
|
|
|
static void destroy_subscriptions(void)
|
|
|
|
{
|
2013-10-03 18:51:33 +00:00
|
|
|
ao2_cleanup(cel_aggregation_topic);
|
|
|
|
cel_aggregation_topic = NULL;
|
|
|
|
ao2_cleanup(cel_topic);
|
|
|
|
cel_topic = NULL;
|
2013-10-02 21:26:34 +00:00
|
|
|
|
Multiple revisions 399887,400138,400178,400180-400181
........
r399887 | dlee | 2013-09-26 10:41:47 -0500 (Thu, 26 Sep 2013) | 1 line
Minor performance bump by not allocate manager variable struct if we don't need it
........
r400138 | dlee | 2013-09-30 10:24:00 -0500 (Mon, 30 Sep 2013) | 23 lines
Stasis performance improvements
This patch addresses several performance problems that were found in
the initial performance testing of Asterisk 12.
The Stasis dispatch object was allocated as an AO2 object, even though
it has a very confined lifecycle. This was replaced with a straight
ast_malloc().
The Stasis message router was spending an inordinate amount of time
searching hash tables. In this case, most of our routers had 6 or
fewer routes in them to begin with. This was replaced with an array
that's searched linearly for the route.
We more heavily rely on AO2 objects in Asterisk 12, and the memset()
in ao2_ref() actually became noticeable on the profile. This was
#ifdef'ed to only run when AO2_DEBUG was enabled.
After being misled by an erroneous comment in taskprocessor.c during
profiling, the wrong comment was removed.
Review: https://reviewboard.asterisk.org/r/2873/
........
r400178 | dlee | 2013-09-30 13:26:27 -0500 (Mon, 30 Sep 2013) | 24 lines
Taskprocessor optimization; switch Stasis to use taskprocessors
This patch optimizes taskprocessor to use a semaphore for signaling,
which the OS can do a better job at managing contention and waiting
that we can with a mutex and condition.
The taskprocessor execution was also slightly optimized to reduce the
number of locks taken.
The only observable difference in the taskprocessor implementation is
that when the final reference to the taskprocessor goes away, it will
execute all tasks to completion instead of discarding the unexecuted
tasks.
For systems where unnamed semaphores are not supported, a really
simple semaphore implementation is provided. (Which gives identical
performance as the original taskprocessor implementation).
The way we ended up implementing Stasis caused the threadpool to be a
burden instead of a boost to performance. This was switched to just
use taskprocessors directly for subscriptions.
Review: https://reviewboard.asterisk.org/r/2881/
........
r400180 | dlee | 2013-09-30 13:39:34 -0500 (Mon, 30 Sep 2013) | 28 lines
Optimize how Stasis forwards are dispatched
This patch optimizes how forwards are dispatched in Stasis.
Originally, forwards were dispatched as subscriptions that are invoked
on the publishing thread. This did not account for the vast number of
forwards we would end up having in the system, and the amount of work it
would take to walk though the forward subscriptions.
This patch modifies Stasis so that rather than walking the tree of
forwards on every dispatch, when forwards and subscriptions are changed,
the subscriber list for every topic in the tree is changed.
This has a couple of benefits. First, this reduces the workload of
dispatching messages. It also reduces contention when dispatching to
different topics that happen to forward to the same aggregation topic
(as happens with all of the channel, bridge and endpoint topics).
Since forwards are no longer subscriptions, the bulk of this patch is
simply changing stasis_subscription objects to stasis_forward objects
(which, admittedly, I should have done in the first place.)
Since this required me to yet again put in a growing array, I finally
abstracted that out into a set of ast_vector macros in
asterisk/vector.h.
Review: https://reviewboard.asterisk.org/r/2883/
........
r400181 | dlee | 2013-09-30 13:48:57 -0500 (Mon, 30 Sep 2013) | 28 lines
Remove dispatch object allocation from Stasis publishing
While looking for areas for performance improvement, I realized that an
unused feature in Stasis was negatively impacting performance.
When a message is sent to a subscriber, a dispatch object is allocated
for the dispatch, containing the topic the message was published to, the
subscriber the message is being sent to, and the message itself.
The topic is actually unused by any subscriber in Asterisk today. And
the subscriber is associated with the taskprocessor the message is being
dispatched to.
First, this patch removes the unused topic parameter from Stasis
subscription callbacks.
Second, this patch introduces the concept of taskprocessor local data,
data that may be set on a taskprocessor and provided along with the data
pointer when a task is pushed using the ast_taskprocessor_push_local()
call. This allows the task to have both data specific to that
taskprocessor, in addition to data specific to that invocation.
With those two changes, the dispatch object can be removed completely,
and the message is simply refcounted and sent directly to the
taskprocessor.
Review: https://reviewboard.asterisk.org/r/2884/
........
Merged revisions 399887,400138,400178,400180-400181 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@400186 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-09-30 18:55:27 +00:00
|
|
|
cel_channel_forwarder = stasis_forward_cancel(cel_channel_forwarder);
|
|
|
|
cel_bridge_forwarder = stasis_forward_cancel(cel_bridge_forwarder);
|
|
|
|
cel_parking_forwarder = stasis_forward_cancel(cel_parking_forwarder);
|
|
|
|
cel_cel_forwarder = stasis_forward_cancel(cel_cel_forwarder);
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
2018-02-17 03:11:42 +00:00
|
|
|
static int unload_module(void)
|
2013-10-02 21:26:34 +00:00
|
|
|
{
|
2013-11-15 14:37:20 +00:00
|
|
|
destroy_routes();
|
2013-10-02 21:26:34 +00:00
|
|
|
destroy_subscriptions();
|
2014-01-24 23:33:26 +00:00
|
|
|
STASIS_MESSAGE_TYPE_CLEANUP(cel_generic_type);
|
2013-10-02 21:26:34 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
ast_cli_unregister(&cli_status);
|
2013-10-02 21:26:34 +00:00
|
|
|
aco_info_destroy(&cel_cfg_info);
|
|
|
|
ao2_global_obj_release(cel_configs);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_global_obj_release(cel_dialstatus_store);
|
|
|
|
ao2_global_obj_release(cel_linkedids);
|
|
|
|
ao2_global_obj_release(cel_backends);
|
2018-02-17 03:11:42 +00:00
|
|
|
|
|
|
|
return 0;
|
2014-01-24 23:33:26 +00:00
|
|
|
}
|
|
|
|
|
2013-10-02 21:26:34 +00:00
|
|
|
/*!
|
|
|
|
* \brief Create the Stasis subscriptions for CEL
|
|
|
|
*/
|
|
|
|
static int create_subscriptions(void)
|
2009-06-26 15:28:53 +00:00
|
|
|
{
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_aggregation_topic = stasis_topic_create("cel_aggregation_topic");
|
|
|
|
if (!cel_aggregation_topic) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cel_topic = stasis_topic_create("cel_topic");
|
|
|
|
if (!cel_topic) {
|
2013-06-13 13:15:56 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cel_channel_forwarder = stasis_forward_all(
|
2013-08-01 13:49:34 +00:00
|
|
|
ast_channel_topic_all_cached(),
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_aggregation_topic);
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!cel_channel_forwarder) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cel_bridge_forwarder = stasis_forward_all(
|
2013-08-01 13:49:34 +00:00
|
|
|
ast_bridge_topic_all_cached(),
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_aggregation_topic);
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!cel_bridge_forwarder) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-13 13:46:40 +00:00
|
|
|
cel_parking_forwarder = stasis_forward_all(
|
|
|
|
ast_parking_topic(),
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_aggregation_topic);
|
2013-06-13 13:46:40 +00:00
|
|
|
if (!cel_parking_forwarder) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_cel_forwarder = stasis_forward_all(
|
|
|
|
ast_cel_topic(),
|
|
|
|
cel_aggregation_topic);
|
|
|
|
if (!cel_cel_forwarder) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-11-15 14:37:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Create the Stasis message router and routes for CEL
|
|
|
|
*/
|
|
|
|
static int create_routes(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
cel_state_router = stasis_message_router_create(cel_aggregation_topic);
|
2013-06-13 13:15:56 +00:00
|
|
|
if (!cel_state_router) {
|
|
|
|
return -1;
|
|
|
|
}
|
2016-06-03 16:35:49 +00:00
|
|
|
stasis_message_router_set_congestion_limits(cel_state_router, -1,
|
|
|
|
6 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
|
2013-06-13 13:15:56 +00:00
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
stasis_cache_update_type(),
|
|
|
|
cel_snapshot_update_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_channel_dial_type(),
|
|
|
|
cel_dial_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_channel_entered_bridge_type(),
|
|
|
|
cel_bridge_enter_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_channel_left_bridge_type(),
|
|
|
|
cel_bridge_leave_cb,
|
|
|
|
NULL);
|
|
|
|
|
2013-06-13 13:46:40 +00:00
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_parked_call_type(),
|
|
|
|
cel_parking_cb,
|
|
|
|
NULL);
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
cel_generic_type(),
|
|
|
|
cel_generic_cb,
|
|
|
|
NULL);
|
|
|
|
|
2013-07-20 13:10:22 +00:00
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_blind_transfer_type(),
|
|
|
|
cel_blind_transfer_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_attended_transfer_type(),
|
|
|
|
cel_attended_transfer_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_call_pickup_type(),
|
|
|
|
cel_pickup_cb,
|
|
|
|
NULL);
|
|
|
|
|
2013-07-20 13:25:05 +00:00
|
|
|
ret |= stasis_message_router_add(cel_state_router,
|
|
|
|
ast_local_optimization_end_type(),
|
|
|
|
cel_local_cb,
|
|
|
|
NULL);
|
|
|
|
|
2013-06-13 13:15:56 +00:00
|
|
|
if (ret) {
|
2013-10-02 21:26:34 +00:00
|
|
|
ast_log(AST_LOG_ERROR, "Failed to register for Stasis messages\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-12-30 04:59:00 +00:00
|
|
|
AO2_STRING_FIELD_HASH_FN(cel_linkedid, id)
|
|
|
|
AO2_STRING_FIELD_CMP_FN(cel_linkedid, id)
|
2014-01-24 23:33:26 +00:00
|
|
|
|
2018-02-17 03:11:42 +00:00
|
|
|
static int load_module(void)
|
2013-10-02 21:26:34 +00:00
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ao2_container *container;
|
|
|
|
|
2017-12-30 04:59:00 +00:00
|
|
|
container = ao2_container_alloc(NUM_APP_BUCKETS, cel_linkedid_hash_fn, cel_linkedid_cmp_fn);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_global_obj_replace_unref(cel_linkedids, container);
|
|
|
|
ao2_cleanup(container);
|
|
|
|
if (!container) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
container = ao2_container_alloc(NUM_DIALSTATUS_BUCKETS,
|
2017-12-30 04:59:00 +00:00
|
|
|
cel_dialstatus_hash_fn, cel_dialstatus_cmp_fn);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_global_obj_replace_unref(cel_dialstatus_store, container);
|
|
|
|
ao2_cleanup(container);
|
|
|
|
if (!container) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (STASIS_MESSAGE_TYPE_INIT(cel_generic_type)) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_cli_register(&cli_status)) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
2017-12-30 04:59:00 +00:00
|
|
|
container = ao2_container_alloc(BACKEND_BUCKETS, cel_backend_hash_fn, cel_backend_cmp_fn);
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_global_obj_replace_unref(cel_backends, container);
|
|
|
|
ao2_cleanup(container);
|
|
|
|
if (!container) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2009-06-26 15:28:53 +00:00
|
|
|
}
|
|
|
|
|
2013-06-13 18:20:31 +00:00
|
|
|
if (aco_info_init(&cel_cfg_info)) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-06-13 18:20:31 +00:00
|
|
|
}
|
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
aco_option_register(&cel_cfg_info, "enable", ACO_EXACT, general_options, "no", OPT_BOOL_T, 1, FLDSET(struct ast_cel_general_config, enable));
|
|
|
|
aco_option_register(&cel_cfg_info, "dateformat", ACO_EXACT, general_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_cel_general_config, date_format));
|
2013-06-13 18:20:31 +00:00
|
|
|
aco_option_register_custom(&cel_cfg_info, "apps", ACO_EXACT, general_options, "", apps_handler, 0);
|
|
|
|
aco_option_register_custom(&cel_cfg_info, "events", ACO_EXACT, general_options, "", events_handler, 0);
|
|
|
|
|
2013-08-22 20:29:15 +00:00
|
|
|
if (aco_process_config(&cel_cfg_info, 0)) {
|
2014-01-24 23:33:26 +00:00
|
|
|
struct cel_config *cel_cfg = cel_config_alloc();
|
2013-08-22 20:29:15 +00:00
|
|
|
|
|
|
|
if (!cel_cfg) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-08-22 20:29:15 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
/* We couldn't process the configuration so create a default config. */
|
2013-08-22 20:29:15 +00:00
|
|
|
if (!aco_set_defaults(&general_option, "general", cel_cfg->general)) {
|
|
|
|
ast_log(LOG_NOTICE, "Failed to process CEL configuration; using defaults\n");
|
2013-09-06 19:26:48 +00:00
|
|
|
ao2_global_obj_replace_unref(cel_configs, cel_cfg);
|
2013-08-22 20:29:15 +00:00
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_ref(cel_cfg, -1);
|
2013-08-22 20:29:15 +00:00
|
|
|
}
|
2013-06-13 18:20:31 +00:00
|
|
|
|
2013-11-15 14:37:20 +00:00
|
|
|
if (create_subscriptions()) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-11-15 14:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_cel_check_enabled() && create_routes()) {
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_FAILURE;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
2018-02-17 03:11:42 +00:00
|
|
|
return AST_MODULE_LOAD_SUCCESS;
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
2018-02-17 03:11:42 +00:00
|
|
|
static int reload_module(void)
|
2013-10-02 21:26:34 +00:00
|
|
|
{
|
|
|
|
unsigned int was_enabled = ast_cel_check_enabled();
|
|
|
|
unsigned int is_enabled;
|
|
|
|
|
|
|
|
if (aco_process_config(&cel_cfg_info, 1) == ACO_PROCESS_ERROR) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
is_enabled = ast_cel_check_enabled();
|
|
|
|
|
|
|
|
if (!was_enabled && is_enabled) {
|
2013-11-15 14:37:20 +00:00
|
|
|
if (create_routes()) {
|
2013-10-02 21:26:34 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (was_enabled && !is_enabled) {
|
2013-11-15 14:37:20 +00:00
|
|
|
destroy_routes();
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ast_verb(3, "CEL logging %sabled.\n", is_enabled ? "en" : "dis");
|
2009-06-26 15:28:53 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-25 13:03:17 +00:00
|
|
|
void ast_cel_publish_event(struct ast_channel *chan,
|
|
|
|
enum ast_cel_event_type event_type,
|
|
|
|
struct ast_json *blob)
|
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_json *cel_blob;
|
|
|
|
struct stasis_message *message;
|
|
|
|
|
2015-11-11 23:20:38 +00:00
|
|
|
cel_blob = ast_json_pack("{s: i, s: o}",
|
2013-06-25 13:03:17 +00:00
|
|
|
"event_type", event_type,
|
2015-11-11 23:20:38 +00:00
|
|
|
"event_details", ast_json_ref(blob));
|
2013-06-25 13:03:17 +00:00
|
|
|
|
stasis: Reduce creation of channel snapshots to improve performance
During some performance testing of Asterisk with AGI, ARI, and lots of Local
channels, we noticed that there's quite a hit in performance during channel
creation and releasing to the dialplan (ARI continue). After investigating
the performance spike that occurs during channel creation, we discovered
that we create a lot of channel snapshots that are technically unnecessary.
This includes creating snapshots during:
* AGI execution
* Returning objects for ARI commands
* During some Local channel operations
* During some dialling operations
* During variable setting
* During some bridging operations
And more.
This patch does the following:
- It removes a number of fields from channel snapshots. These fields were
rarely used, were expensive to have on the snapshot, and hurt performance.
This included formats, translation paths, Log Call ID, callgroup, pickup
group, and all channel variables. As a result, AMI Status,
"core show channel", "core show channelvar", and "pjsip show channel" were
modified to either hit the live channel or not show certain pieces of data.
While this is unfortunate, the performance gain from this patch is worth
the loss in behaviour.
- It adds a mechanism to publish a cached snapshot + blob. A large number of
publications were changed to use this, including:
- During Dial begin
- During Variable assignment (if no AMI variables are emitted - if AMI
variables are set, we have to make snapshots when a variable is changed)
- During channel pickup
- When a channel is put on hold/unhold
- When a DTMF digit is begun/ended
- When creating a bridge snapshot
- When an AOC event is raised
- During Local channel optimization/Local bridging
- When endpoint snapshots are generated
- All AGI events
- All ARI responses that return a channel
- Events in the AgentPool, MeetMe, and some in Queue
- Additionally, some extraneous channel snapshots were being made that were
unnecessary. These were removed.
- The result of ast_hashtab_hash_string is now cached in stasis_cache. This
reduces a large number of calls to ast_hashtab_hash_string, which reduced
the amount of time spent in this function in gprof by around 50%.
#ASTERISK-23811 #close
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/3568/
........
Merged revisions 416211 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@416216 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-06-13 18:24:49 +00:00
|
|
|
message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), cel_generic_type(), cel_blob);
|
2013-06-25 13:03:17 +00:00
|
|
|
if (message) {
|
|
|
|
stasis_publish(ast_cel_topic(), message);
|
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_cleanup(message);
|
|
|
|
ast_json_unref(cel_blob);
|
2013-06-25 13:03:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct stasis_topic *ast_cel_topic(void)
|
|
|
|
{
|
|
|
|
return cel_topic;
|
|
|
|
}
|
2013-07-02 14:01:53 +00:00
|
|
|
|
|
|
|
struct ast_cel_general_config *ast_cel_get_config(void)
|
|
|
|
{
|
|
|
|
RAII_VAR(struct cel_config *, mod_cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
|
2013-08-22 17:13:16 +00:00
|
|
|
|
2013-08-22 19:52:59 +00:00
|
|
|
if (!mod_cfg || !mod_cfg->general) {
|
2013-08-22 17:13:16 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:01:53 +00:00
|
|
|
ao2_ref(mod_cfg->general, +1);
|
|
|
|
return mod_cfg->general;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_cel_set_config(struct ast_cel_general_config *config)
|
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
int was_enabled;
|
2013-10-02 21:26:34 +00:00
|
|
|
int is_enabled;
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ast_cel_general_config *cleanup_config;
|
|
|
|
struct cel_config *mod_cfg = ao2_global_obj_ref(cel_configs);
|
2013-08-22 17:13:16 +00:00
|
|
|
|
2013-08-22 19:52:59 +00:00
|
|
|
if (mod_cfg) {
|
2014-01-24 23:33:26 +00:00
|
|
|
was_enabled = ast_cel_check_enabled();
|
|
|
|
|
|
|
|
cleanup_config = mod_cfg->general;
|
|
|
|
ao2_bump(config);
|
2013-08-22 19:52:59 +00:00
|
|
|
mod_cfg->general = config;
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_cleanup(cleanup_config);
|
2013-10-02 21:26:34 +00:00
|
|
|
|
|
|
|
is_enabled = ast_cel_check_enabled();
|
|
|
|
if (!was_enabled && is_enabled) {
|
2013-11-15 14:37:20 +00:00
|
|
|
create_routes();
|
2013-10-02 21:26:34 +00:00
|
|
|
} else if (was_enabled && !is_enabled) {
|
2013-11-15 14:37:20 +00:00
|
|
|
destroy_routes();
|
2013-10-02 21:26:34 +00:00
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
|
|
|
|
ao2_ref(mod_cfg, -1);
|
2013-08-22 17:13:16 +00:00
|
|
|
}
|
2013-07-02 14:01:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-17 14:46:44 +00:00
|
|
|
int ast_cel_backend_unregister(const char *name)
|
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
struct ao2_container *backends = ao2_global_obj_ref(cel_backends);
|
2013-08-17 14:46:44 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (backends) {
|
|
|
|
ao2_find(backends, name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
|
|
|
|
ao2_ref(backends, -1);
|
2013-08-17 14:46:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
|
|
|
|
{
|
2014-01-24 23:33:26 +00:00
|
|
|
RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
|
|
|
|
struct cel_backend *backend;
|
2013-08-17 14:46:44 +00:00
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
if (!backends || ast_strlen_zero(name) || !backend_callback) {
|
2013-08-17 14:46:44 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
/* The backend object is immutable so it doesn't need a lock of its own. */
|
|
|
|
backend = ao2_alloc_options(sizeof(*backend) + 1 + strlen(name), NULL,
|
|
|
|
AO2_ALLOC_OPT_LOCK_NOLOCK);
|
2013-08-17 14:46:44 +00:00
|
|
|
if (!backend) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-24 23:33:26 +00:00
|
|
|
strcpy(backend->name, name);/* Safe */
|
2013-08-17 14:46:44 +00:00
|
|
|
backend->callback = backend_callback;
|
|
|
|
|
2014-01-24 23:33:26 +00:00
|
|
|
ao2_link(backends, backend);
|
|
|
|
ao2_ref(backend, -1);
|
2013-08-17 14:46:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-02-17 03:11:42 +00:00
|
|
|
|
|
|
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "CEL Engine",
|
|
|
|
.support_level = AST_MODULE_SUPPORT_CORE,
|
|
|
|
.load = load_module,
|
|
|
|
.unload = unload_module,
|
|
|
|
.reload = reload_module,
|
|
|
|
.load_pri = AST_MODPRI_CORE,
|
2018-07-26 17:52:46 +00:00
|
|
|
.requires = "extconfig",
|
2018-02-17 03:11:42 +00:00
|
|
|
);
|